platform-independent /dev/null output sink for Java - java

Other than an anonymous class (new OutputStream() { ... }), can anyone suggest a moral equivalent of new FileOutputStream("/dev/null") that also works on Windows?
In case someone's wondering 'what's this for?'
I have a program that does a consistency analysis on a file. It has a 'verbose' option. When the verbose option is on, I want to see a lot of output. The program is not in a hurry, it's a tool, so instead of writing all those extra if statements to test if I want the output, I just want to write it to the bit-bucket when not desired.

You can use NullOutputStream from apache commons
https://commons.apache.org/proper/commons-io/javadocs/api-2.4/org/apache/commons/io/output/NullOutputStream.html
Or just implement your own
package mypackage;
import java.io.OutputStream;
import java.io.IOException;
public class NullOutputStream extends OutputStream {
public void write(int i) throws IOException {
//do nothing
}
}

NUL works for Windows NT, but that doesn't work in *NIX.
output = new FileOutputStream("NUL");
Better use NullOutputStream of the Commons IO instead to be platform independent.

Starting from JDK 11, you can use a new static method on OutputStream - nullOutputStream.
var out = OutputStream.nullOutputStream();

As long as you don't care about the slight performance difference (and you don't feel like you might run out of space), you can just use "/dev/null". On Windows, it will create a real file on whatever your current drive is (e.g., c:\dev\null).

Related

java InputStreamReader / BufferedReader "read" behavior

A related question is this one: Where is the specification that defines this behavior for InputStreamReader?, but I'm not sure if it answers mine...
Please note, I'm just experimenting with the language.
I have this code:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class Capitalize {
public static void main(String[] args) {
try (BufferedReader br = new BufferedReader(new InputStreamReader(
System.in))) {
char c;
do {
c = Character.toUpperCase((char) br.read());
System.out.print(c);
} while (c != (char) -1);
} catch (IOException e) {
e.printStackTrace(System.err);
}
}
}
Using ubuntu linux, I was expecting the output to be like this:
fFoOoO bBaArR
but instead, it's like this:
foo bar (line feed)
FOO BAR (waits for more characters or Ctrl + D)
Right now I'm not sure about what is the behavior in windows, probably it's different, but still, this confuses me a bit. Reading the documentation for the read method, I see it will only return -1 if the end of stream is reached. I kind of understand how that would work for reading a file, but how about, in this case, the console? Why does it have to wait until the Ctrl + D is typed? Is there any way to get to the end of the stream without having to type Ctrl + D? Is there a way to achieve what I was expecting?
Thanks in advance
As EJP comments, this is nothing to do with InputStream / BufferedReader. What you are seeing is the behaviour of the Linux "tty" input drivers and a typical console command.
What you are seeing is normal "line editing" or "line buffering". The input is only made available for Java to read (via the stdin file descriptor) when you press the <Enter> key. The output you are seeing prior to that is the character echoing from the "tty" drivers. (And notice that if you enter <Backspace> or whatever ... the characters get erased. When you type <Enter>, Java doesn't see the backspaces, etc. They have been edited out.)
Input via the java.io.Console class behaves the same way.
This is the same on Windows: the behaviour is portable. (This is a good thing ...)
If you want your Java application to see the characters as they are typed, when they are typed, you will need to use 3rd-party libraries (or native code) to implement this. (In Linux, it entails switching the tty driver into "raw" mode ... and this functionality is non-portable, and not supported by standard Java SE.)

Calling KeyTool, redirecting System.out has no effect

So we want to use the bog-standard keytool utility that ships with a JRE. But rather than going through the trouble of finding the correct path and executable extension, spawning a subprocess, and running the executable, we collectively had the bright idea ("remember, none of us is as dumb as all of us!") to just call KeyTool's main() directly. It's implemented in Java code and also shipped with the JRE, and contains the standard "classpath" exception to the GPL so we can link against it.
Looking at the KeyTool source, there's even some provision made for this sort of thing: there are comments like "if you're calling KeyTool.main() directly in your own Java program, then [helpful reminder]" and the top-level main() is capable of propagating exceptions to calling code instead of just dying with System.exit(). Being able to just build the same command-line argument array and run KeyTool.main(stuff) instead of having to mess with platform differences seems like a very Java-esque thing to do, right?
In practice, weird things happen and we don't know why.
We want to capture any output from running KeyTool, which starts off like this:
// jdk/src/share/classes/sun/security/tools/KeyTool.java, line 331:
public static void main(String[] args) throws Exception {
KeyTool kt = new KeyTool();
kt.run(args, System.out);
}
private void run(String[] args, PrintStream out) throws Exception {
// real code here, sends to 'out'
}
The KeyTool entry points don't allow us to pass a PrintStream, it's hardcoded to use System.out. That should be okay thanks to System.setOut. We have an OutputStream subclass which feeds to a JTextComponent, but for initial coding, redirecting to a text file is fine. So our code does
PrintStream orig = System.out;
try {
System.out.println("This is the last visible console line");
System.setOut(new PrintStream("redirect_test.txt"));
System.out.println("This is now redirected!");
KeyTool.main(keytool_argv); // "-help" and "-debug" for now
}
catch all the myriad ways things might go wrong { ... }
finally {
System.setOut(orig);
System.out.println("Back to normal console output");
}
But when we run the code, the redirect_test.txt file contains only "This is now redirected!". The output from keytool's "-help" still shows up on the console, along with the before-and-after println calls.
There are some other oddities in calling KeyTool directly, like the package and class name has changed between Java 7 and Java 8, but that's easy to deal with via reflection. (The comments in the KeyTool source in Java 8 still refer to the Java 7 name, heh.) The only thing just freaky weird is how its "System.out" is strangely not affected by the same redirection that works everywhere else. (No, there are no weird import statements bringing in a special System replacement.)
Here's an online copy of Java 7's KeyTool.java if you don't happen to have OpenJDK sitting around.
You just need to redirect both System.out and System.err, since the usage instructions get printed to the standard error stream instead of the standard output stream. Try this:
PrintStream original = System.out;
PrintStream redirected = new PrintStream("redirect_test.txt")
try {
System.out.println("This is the last visible console line");
System.setOut(redirected);
System.setErr(redirected);
System.out.println("This is now redirected!");
KeyTool.main(keytool_argv); // "-help" and "-debug" for now
}
catch all the myriad ways things might go wrong { ... }
finally {
System.setOut(original);
System.setErr(original);
System.out.println("Back to normal console output");
}

Java 1.4.2 File.listFiles not working properly with CIFS mounts - workaround?

I'm using Java 1.4.2 and Debian 6.0.3. There's a shared Windows folder in the network, which is correctly mounted to /mnt/share/ via fstab (e.g. it's fully visible from OS and allows all operations) using CIFS. However, when I try to do this in Java:
System.out.println(new File("/mnt/share/").listFiles().length)
it would always return 0, meaning File[] returned by listFiles is empty. The same problem applies to every subdirectory of /mnt/share/. list returns empty array as well. Amusingly enough, other File functions like "create", "isDirectory" or even "delete" work fine. Directories mounted from USB flash drive (fat32) also work fine.
I tested this on 2 different "shared folders" from different Windows systems; one using domain-based authentication system, another using "simple sharing" - that is, guest access. The situation seems weird, since mounted directories should become a part of a file system, so any program could use it. Or so I thought, at least.
I want to delete a directory in my program, and I currently see no other way of doing it except recursive walking on listFiles, so this bug becomes rather annoying. The only "workaround" I could think of is to somehow run an external bash script, but it seems like a terrible solution.
Edit: It seems this is 1.4.2-specific bug, everything works fine in Java 6. But I can't migrate, so the problem remains.
Could you suggest some workaround? Preferably without switching to third-party libs instead of native ones, I can't say I like the idea of rewriting the whole project for the sake of single code line.
Since Java 1.2 there is method File.getCanonicalFile(). In your case with mounted directory you should use exactly this one in such style:
new File("/mnt/share/").getCanonicalFile().listFiles()
So, two and half years later after giving up I encounter the same problem, again stuck with 1.4.2 because I need to embed the code into obsolete Oracle Forms 10g version.
If someone, by chance, stumbles onto this problem and decides to solve it properly, not hack his way through, it most probably has to do with (highly) unusual inode mapping that CIFS does upon mounting the remote filesystem, causing more obscure bugs some of which can be found on serverfault. One of the side-effects of such mapping is that all directories have zero hard-link count. Another one is that all directories have "size" of exactly 0, instead of usual "sector size or more", which can be checked even with ls.
I can't be sure without examining the (proprietary) source code, but I can guess that Java prior to 1.5 used some shortcut like checking link count internally instead of actually calling readdir() with C, which works equally well for any mounted FS.
Anyway, the second side-effect can be used to create a simple wrapper around File which won't rely on system calls unless it suspects a directory is mounted using CIFS. Other versions of list and listFiles functions in java.io.File, even ones using filters, rely on list() internally, so it's OK to override only it.
I didn't care about listFiles returning File[] not FileEx[] so I didn't bother to override it, but is should be simple enough. Obviously, that code can work only in Unix-like systems having ls command handy.
package FSTest;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
public class FileEx extends File
{
public FileEx(String path)
{
super(path);
}
public FileEx(File f)
{
super(f.getAbsolutePath());
}
public String[] list()
{
if (this.canRead() && this.isDirectory())
{
/*
* Checking the length of dir is not the most reliable way to distinguish CIFS mounts.
* However, zero directory length generally indicates something unusual,
* so calling ls on it wouldn't hurt. Ordinary directories don't suffer any overhead this way.
* If this "zero-size" behavior is ever changed by CIFS but list() still won't work,
* it will be safer to call super.list() first and call this.listUsingExec if returned array has 0 elements.
* Though it might have serious performance implications, of course.
*/
if (this.length() > 0)
return super.list();
else
return this.listUsingExec();
}
else
return null;
}
private String[] listUsingExec()
{
Process p;
String command = "/bin/ls -1a " + this.getAbsolutePath();
ArrayList list = new ArrayList();
try
{
p = Runtime.getRuntime().exec(command);
p.waitFor();
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
for (String line = reader.readLine(); line != null; line = reader.readLine())
{
if (!line.equalsIgnoreCase(".") && !line.equalsIgnoreCase(".."))
list.add(line);
}
String[] ret = new String[list.size()];
list.toArray(ret);
return ret;
}
catch (IOException e)
{
return null;
}
}
}

Java DTrace bridge on OS X

I am trying to grab filesystem events on OS / Kernel level on OS X.
There are 2 requirements i have to follow. The first one is to do this in java as the whole project im developing for is written in java. The second one is that i have to find out when a document is opened.
For Linux I used inotify-java, but I can't find a good equivalent on OS X. Also the JNA doesn't provide a helpful binding. Currently I'm avoiding catching events by frequently calling the lsof program. This, however, is a bad solution.
Thanks for the help.
You can use dtrace on OSX, but since it needs root privileges it's not something you'd want to put into a runtime of a system.
In any case, you won't be able to do this in pure Java (any Java API would be a wrapper around some lower level C introspection, and if you're doing it kernel-wide, would need to be done as root).
If you just want to track when your program is opening files (as opposed to other files on the same system) then you can install your own Security Manager and implement the checkRead() family of methods, which should give you an idea of when accesses are happening.
import java.io.*;
public class Demo {
public static void main(String args[]) throws Exception {
System.setSecurityManager(new Sniffer());
File f = new File("/tmp/file");
new FileInputStream(f);
}
}
class Sniffer extends SecurityManager {
public void checkRead(String name) {
System.out.println("Opening " + name);
}
}

Calling a java program from another

How do i call a Java command from a stand alone java program.
I understand that Runtime.getRuntime().exec("cmd c/ javac <>.java"); would work. However, this would be platform specific.
Any other APIs available that could make it work in j2sdk1.4 ?
If you can run everything in the same JVM, you could do something like this:
public class Launcher {
...
public static void main(String[] args) throws Exception {
launch(Class.forName(args[0]), programArgs(args, 1));
}
protected static void launch(Class program, String[] args) throws Exception {
Method main = program.getMethod("main", new Class[]{String[].class});
main.invoke(null, new Object[]{args});
}
protected static String[] programArgs(String[] sourceArgs, int n) {
String[] destArgs = new String[sourceArgs.length - n];
System.arraycopy(sourceArgs, n, destArgs, 0, destArgs.length);
return destArgs;
}
And run it with a command line like this:
java Launcher OtherClassWithMainMethod %CMD_LINE_ARGS%
Calling Runtime.getRuntime().exec() is not only platform specific, it is extremely inefficient. It will result in spawning a brand new shell and an entire jvm which could potentially be very expensive depending on the dependencies of this application (no pun intended).
The best way to execute "external" Java code would be to place it in your CLASSPATH. If you must call an application's main method you can simply import and call the method directly. This could be done like so:
import my.externals.SomeMain
// call as if we are running from console
SomeMain.main(new String[] {"some", "console", "arguments"})
Of course, the best case scenario would be to simply use this as an external library and access the code you need without having to call SomeMain.main(). Adhering to best practices and writing proper encapsulated modular objects allows for much greater portability and ease of use when being used by other applications.
When you leave the JVM and move to system commands, then you have to deal with the platform specific commands yourself. The JVM offers a good way for abstraction, so why move away?
If you want to execute java specific binaries, check out the ant libraries of java. You can execute ant scripts from java which execute platform depending commands.
Java programming from quercus php on GAE:
import com.newatlanta.commons.vfs.provider.gae.GaeVFS;
import org.apache.commons.io.IOUtils;
import java.lang.Long;
import java.lang.Boolean;
GaeVFS::setRootPath(quercus_servlet_request()->getSession(true)->getServletContext()->getRealPath('/'));
define('VFSM', GaeVFS::getManager());
//VFSM->resolveFile('gae://gaevfs')->createFolder();
$file=VFSM->resolveFile('gae://gaevfs/tmp1');
//$file->createFile();
$text='pp';
$method=$file->getClass()->getDeclaredMethod('updateContentSize', array(Long::TYPE, Boolean::TYPE));
$method->setAccessible(true);
$method->invoke($file, strlen($text), true);
$out=$file->getContent()->getOutputStream();
IOUtils::write($text, $out, 'UTF8');
$out->close();
$in=$file->getContent()->getInputStream();
$method=$file->getClass()->getDeclaredMethod('doGetContentSize',array());
$method->setAccessible(true);
$len=$method->invoke($file);
$whole=IOUtils::toString($in, 'UTF8').':'.$len."<br>";
$in->close();
echo $whole;
GaeVFS::clearFilesCache();
GaeVFS::close();

Categories

Resources