NOTE: Please run the exact code below; no adaptations of it, in particular, do not use File, as this bug is tied to the new java.nio.file API
OK, this is not really a "question which is in need of an answer" but rather a call for witnesses...
Scenario:
have a directory on your OS, whatever it is, which you know you have privileges to access -- in Unix parlance, you have at least read access to it (which means you can list the entries in it); in the code below, it is supposed that the path represented by System.getProperty("java.io.tmpdir") fits the bill;
have a Oracle JDK, or OpenJDK, 7+ installed; so that you have java.nio.file at your disposal.
Now, what the code below does is pretty simple: it tries to open a new InputStream on this directory using Files.newInputStream(). Code (also available here; added comments mine):
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
public final class Main
{
public static void main(final String... args)
throws IOException
{
final Path path = Paths.get(System.getProperty("java.io.tmpdir"));
try (
final InputStream in = Files.newInputStream(path); // FAIL_OPEN
) {
final byte[] buf = new byte[1024];
int bytesRead;
while ((bytesRead = in.read(buf)) != -1) // FAIL_READ
System.out.printf("%d bytes read\n", bytesRead);
}
}
}
OK, now when you run that code, this is what happens for the following JRE/OS combinations:
Linux x86_64, Oracle JDK 1.8.0_25: IOException (is a directory) at FAIL_READ;
Linux x86_64, Oracle JDK 1.7.0_72: IOException (is a directory) at FAIL_READ;
Mac OS X x86_64, Oracle JDK 1.8.0_25: IOException (is a directory) at FAIL_READ;
Windows 7, Oracle JDK 1.8.0_25: AccessDeniedException at FAIL_OPEN (!!).
Honestly, I don't know what to do with that piece of code. As I said in the introduction, I am looking for witnesses here. I will certainly open a bug to OpenJDK about this, it seems pretty serious. I also mailed the nio-dev mailing list about this problem.
Well, as to a question I'd have one: what about a IsDirectoryException in the JDK (inheriting FileSystemException)? I have actually defined it in one of my projects to account for such a problem. I am not sure why this problem was not considered by the "Java guys"...
My observations (sorry, no other systems around here atm, later I might add ARM):
JDK 1.8.0_25, Linux x86_64: java.io.IOException: Is a directory at // FAIL_READ.
I agree that this behavior is unexpected, it should not be possible to create an InputStream from a directory in the first place. I suggest you file this as a bug. Even if Files.newInputStream doesn't state it explicitly, the behavior is inconsistent with the rest of the API.
Related
I've been trying to find ways to obtain the Thread Dump from a Java Application on my Windows server running on jre 1.8.0_144.
Non of the monitoring utilities like jcmd jstack jconsole are available in either the bin or lib folders of the Java environment directory.
I have come across several applications online that claim to perform the same task but haven't found a reliable one yet.
Changing the JRE version, unfortunately, has been ruled out as an option
There is a way if you are running with tools.jar available, that is running from a JDK instead of a stock JRE. However given that you don't have the jcmd, jstack, jconsole tools available, this is unlikely.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Method;
// You need to add tools.jar to the classpath to get this
import com.sun.tools.attach.VirtualMachine;
public class Main {
public static void main(String[] args) throws Exception {
// Here you will need to use JNI or JNA to get the current process' PID
// because the JRE version you are using doesn't have any other way.
long pid = getCurrentPid(); // you need to create this method
VirtualMachine vm = VirtualMachine.attach(""+pid);
Method m = vm.getClass().getMethod("remoteDataDump", Object[].class);
InputStream in = (InputStream) m.invoke(vm, new Object[] { new Object[0] } ); // awkward due to nested var-args
try (BufferedReader buffer = new BufferedReader(new InputStreamReader(in))) {
buffer.lines().forEach(System.out::println);
}
}
}
Is there a way to check if a specific program is installed on Windows using Java?
I'm trying to develop a Java program that automatically creates zip archives by using the code line command from 7-Zip.
So, I would like to check in Java if on my windows OS '7-Zip' is already installed. No check for running apps or if OS is Windows or Linux. I want to get a bool (true/false) if '7-Zip' is installed on Windows.
The library Apache Commons has a class called SystemUtils - full documentation is available at https://commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/apache/commons/lang/SystemUtils.html.
In this library you have the following static boolean properties at your disposal:
SystemUtils.IS_OS_LINUX
SystemUtils.IS_OS_WINDOWS
The unix-like solution would be to simply try to run the program with --version flag (on windows probably the /? or - like in the 7zip case - without any at all) and check whether it fails, or what the return code will be.
Something like:
public boolean is7zipInstalled() {
try {
Process process = Runtime.getRuntime().exec("7zip.exe");
int code = process.waitFor();
return code == 0;
} catch (Exception e) {
return false;
}
}
I assume that you're talking about Windows. As Java is intended to be a platform-independent language and the way how to determine it differs per platform, there's no standard Java API to check that. You can however do it with help of JNI calls on a DLL which crawls the Windows registry. You can then just check if the registry key associated with the software is present in the registry. There's a 3rd party Java API with which you can crawl the Windows registry: jRegistryKey.
Here's an SSCCE with help of jRegistryKey:
package com.stackoverflow.q2439984;
import java.io.File;
import java.util.Iterator;
import ca.beq.util.win32.registry.RegistryKey;
import ca.beq.util.win32.registry.RootKey;
public class Test {
public static void main(String... args) throws Exception {
RegistryKey.initialize(Test.class.getResource("jRegistryKey.dll").getFile());
RegistryKey key = new RegistryKey(RootKey.HKLM, "Software\\Mozilla");
for (Iterator<RegistryKey> subkeys = key.subkeys(); subkeys.hasNext();) {
RegistryKey subkey = subkeys.next();
System.out.println(subkey.getName()); // You need to check here if there's anything which matches "Mozilla FireFox".
}
}
}
If you however intend to have a platformindependent application, then you'll also have to take into account the Linux/UNIX/Mac/Solaris/etc. (in other words: anywhere where Java is able to run) ways to detect whether FF is installed. Else you'll have to distribute it as a Windows-only application and do a System#exit() along with a warning whenever System.getProperty("os.name") is not Windows.
Sorry, I don't know how to detect in other platforms whether FF is installed or not, so don't expect an answer from me for that ;)
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;
}
}
}
For this two imports;
import sun.misc.BASE64Encoder;
import sun.misc.BASE64Decoder;
I got this error:
Access restriction: The type BASE64Decoder is not accessible due to restriction on required library C:\Program Files\Java\jre6\lib\rt.jar
How can I resolve this error?
Go to Window-->Preferences-->Java-->Compiler-->Error/Warnings.
Select Deprecated and Restricted API. Change it to warning.
Change forbidden and Discouraged Reference and change it to warning. (or as your need.)
That error is caused by your Eclipse configuration. You can reduce it to a warning. Better still, use a Base64 encoder that isn't part of a non-public API. Apache Commons has one, or when you're already on Java 1.8, then use java.util.Base64.
Sure - just don't use the Sun base64 encoder/decoder. There are plenty of other options available, including Apache Codec or this public domain implementation.
Then read why you shouldn't use sun.* packages.
Java 6 ships the javax.xml.bind.DatatypeConverter. This class provides two static methods that support the same decoding & encoding:
parseBase64Binary() / printBase64Binary()
Update:
Since Java 8 we now have a much better Base64 Support.
Use this and you will not need an extra library, like Apache Commons Codec.
I had this problem on jdk1.6.0_37.
This is the only JDE/JRE on my system. I don't know why, but the following solved the problem:
Project -> Properties -> Java Build Path - > Libraries
Switch radio button from Execution environment to Alernate JRE.
This selects the same jdk1.6.0_37, but after clean/build the compile error disappeared.
Maybe clarification in answer from ram (Mar 16 at 9:00) has to do something with that.
Yup, and sun.misc.BASE64Decoder is way slower: 9x slower than java.xml.bind.DatatypeConverter.parseBase64Binary() and 4x slower than org.apache.commons.codec.binary.Base64.decodeBase64(), at least for a small string on Java 6 OSX.
Below is the test program I used. With Java 1.6.0_43 on OSX:
john:password = am9objpwYXNzd29yZA==
javax.xml took 373: john:password
apache took 612: john:password
sun took 2215: john:password
Btw that's with commons-codec 1.4. With 1.7 it seems to get slower:
javax.xml took 377: john:password
apache took 1681: john:password
sun took 2197: john:password
Didn't test Java 7 or other OS.
import javax.xml.bind.DatatypeConverter;
import org.apache.commons.codec.binary.Base64;
import java.io.IOException;
public class TestBase64 {
private static volatile String save = null;
public static void main(String argv[]) {
String teststr = "john:password";
String b64 = DatatypeConverter.printBase64Binary(teststr.getBytes());
System.out.println(teststr + " = " + b64);
try {
final int COUNT = 1000000;
long start;
start = System.currentTimeMillis();
for (int i=0; i<COUNT; ++i) {
save = new String(DatatypeConverter.parseBase64Binary(b64));
}
System.out.println("javax.xml took "+(System.currentTimeMillis()-start)+": "+save);
start = System.currentTimeMillis();
for (int i=0; i<COUNT; ++i) {
save = new String(Base64.decodeBase64(b64));
}
System.out.println("apache took "+(System.currentTimeMillis()-start)+": "+save);
sun.misc.BASE64Decoder dec = new sun.misc.BASE64Decoder();
start = System.currentTimeMillis();
for (int i=0; i<COUNT; ++i) {
save = new String(dec.decodeBuffer(b64));
}
System.out.println("sun took "+(System.currentTimeMillis()-start)+": "+save);
} catch (Exception e) {
System.out.println(e);
}
}
}
This error is because of you are importing below two classes
import sun.misc.BASE64Encoder; import sun.misc.BASE64Decoder;. Maybe you are using encode and decode of that library like below.
new BASE64Encoder().encode(encVal);
newBASE64Decoder().decodeBuffer(encryptedData);
Yeah instead of sun.misc.BASE64Encoder you can import
java.util.Base64 class.Now change the previous encode method as below:
encryptedData=Base64.getEncoder().encodeToString(encryptedByteArray);
Now change the previous decode method as below
byte[] base64DecodedData = Base64.getDecoder().decode(base64EncodedData);
Now everything is done , you can save your program and run. It will run without showing any error.
Go to the Build Path settings in the project properties.
Remove the JRE System Library
Add it back; Select "Add Library" and select the JRE System Library. The default worked for me.
This works because you have multiple classes in different jar files. Removing and re-adding the jre lib will make the right classes be first. If you want a fundamental solution make sure you exclude the jar files with the same classes.
Be careful, sun.misc.BASE64Decoder is not available in JDK-13
This error (or warning in later versions) occurs because you are compiling against a Java Execution Environment. This shows up as JRE System library [CDC-1.0/Foundation-1.0] in the Build path of your Eclipse Java project. Such environments only expose the Java standard API instead of all the classes within the runtime. This means that the classes used to implement the Java standard API are not exposed.
You can allow access to these particular classes using access rules, you could configure Eclipse to use the JDK directly or you could disable the error. You would however be hiding a serious error as Sun internal classes shouldn't be used (see below for a short explanation).
Java contains a Base64 class in the standard API since Java 1.8. See below for an example how to use it:
Java 8 import statement:
import java.util.Base64;
Java 8 example code:
// create a byte array containing data (test)
byte[] binaryData = new byte[] { 0x64, 0x61, 0x74, 0x61 };
// create and configure encoder (using method chaining)
Base64.Encoder base64Encoder = Base64.getEncoder().withoutPadding();
// encode to string (instead of a byte array containing ASCII)
String base64EncodedData = base64Encoder.encodeToString(binaryData);
// decode using a single statement (no reuse of decoder)
// NOTE the decoder won't fail because the padding is missing
byte[] base64DecodedData = Base64.getDecoder().decode(base64EncodedData);
If Java 8 is not available a library such as Apache Commons Codec or Guava should be used.
Sun internal classes shouldn't be used. Those classes are used to implement Java. They have got public methods to allow instantiation from other packages. A good build environment however should protect you from using them.
Using internal classes may break compatibility with future Java SE runtimes; the implementation and location of these classes can change at any time. It should be strongly discouraged to disable the error or warning (but the disabling of the error is suggested in previous answers, including the two top voted ones).
I am using unix system.
In eclipse project-> Properties -> Java Compiler -> Errors/Warning -> Forbidden Access(access rule) -> Turn it to warning/Ignore(Previously it was set to Error).
Was getting sun.misc.base64encoder cannot be resolved to a type on eclipse -
Resolved this by pointing eclipse to use Java version 1.8 installed on system -
Windows -> Preferences -> Java -> Installed JREs -> add jre path (eg: C:\Program Files\Java\jdk1.8.0_271)
Right-click on project -> Maven -> Update project
This will resolve then.
I know this is very Old post. Since we don't have any thing sun.misc in maven
we can easily use
StringUtils.newStringUtf8(Base64.encodeBase64(encVal));
From org.apache.commons.codec.binary.Base64
solution: go into java 8 sdk fodler, from jre\lib\rt.jar copy to sdklib.jar (it is somewhere in eclipse folder) classes (with same paths):
sun/misc/BASE64Decoder.class,
sun/misc/BASE64Encoder.class,
sun/misc/CharacterDecoder.class,
sun/misc/CharacterEncoder.class
that's all
Add base64decoder jar and try these imports:
import Decoder.BASE64Decoder;
import Decoder.BASE64Encoder;
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);
}
}