How to check file permissions in Java (OS independently) - java

I have the following snippet of code:
public class ExampleClass {
public static void main(String[] args) throws FileNotFoundException {
String filePath = args[0];
File file = new File(filePath);
if (!file.exists())
throw new FileNotFoundException();
if (file.canWrite())
System.out.println(file.getAbsolutePath() + ": CAN WRITE!!!");
else
System.out.println(file.getAbsolutePath() + ": CANNOT WRITE!!!!!");
if (file.canRead())
System.out.println(file.getAbsolutePath() + ": CAN READ!!!");
else
System.out.println(file.getAbsolutePath() + ": CANNOT READ!!!!!");
if (file.canExecute())
System.out.println(file.getAbsolutePath() + ": CAN EXECUTE!!!");
else
System.out.println(file.getAbsolutePath() + ": CANNOT EXECUTE!!!!!");
}
}
It works in Linux OS, but the problem is that it doesn't work in windows7. So the question is: Does anybody know a method to check privileges to a file in Java OS INDEPENDENTLY?

This might be caused by something (for instance an anti-virus product) "mediating" file access in an inconsistent way.
Certainly, it is hard to believe that the Java File.canXxxx() methods are generally broken on any flavour of Windows.
UPDATE - I take that back. Read this Sun bug report ... and weep. The short answer is that it is a Windows bug, and Sun decided not to work around it. (But the new Java 7 APIs do work ...)
FWIW, I maintain that it is BAD PRACTICE to try to check file access permissions like that. It is better to simply attempt to use the file, and catch the exceptions if / when they occur. See https://stackoverflow.com/a/6093037/139985 for my reasoning. (And now we have another reason ...)

I have done some tests on the NIO APIs (from Java 7) and they seem to work perfectly.
import java.io.FileNotFoundException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
public class PermissionCheck {
public static void main(String[] args) throws FileNotFoundException {
String filePath = args[0];
Path p = Paths.get(filePath);
if (Files.notExists(p))
throw new FileNotFoundException();
if (Files.isWritable(p))
...
if (Files.isReadable(p))
...
if (Files.isExecutable(p))
...
}
}
JDKs: 1.7.0_25, 1.8.0_91
OS: Windows 7, 8 (64bit)

First of all, Java trust local files and untrust remote files by default and by design. So when testing, be aware of that what you can do in your computer at home, may be impossible in some remote drive of your company's server.
Second, when we check file permissions on remote drives, it's usually not enough just setting it in the Windows Explorer(Property... - Read only/Hide/Archive, etc. ). For example, my organization have other mechinisms to control both local and remote file permission, and even being Administrator of my PC cannot guarantee everything. Even if manually/programmatically you can change the permission of a file, if some other applications/group policy/etc forbids you to do so, the change may fail. (For example, setReadable() returns false, suggesting that it's not possible)For example, I can execute a txt file in a remote directory, meaning open it, but a bat file in the same directory is not executable, actually, in my case, I am required to ask my admin to gain more authority when I want to create a bat file. I think it might be that bat extension are forbidden. Because as user in some user group in Windows, your action and JVM run by you are limited by higher rules than JVM itself. Correct me if I am wrong.
However, even if you might not be able to set the permisson of a file, now you can read them correctly in Java 7. Obviously after the bug report, Java guys had done something to fix the most of it. I am using jdk 1.7.0_19, and to test, I have done the following:
Set the property of a remote file, in Windows Explorer, to Read Only and Hidden.
Read it from Java, with code below (from the link of Stephen C and modified to see setXxxxx() methods can work).
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
public class FilePermissionTester {
public static void main( String[] args ) throws IOException {
File file = new File("Y:\\some\\remote\\drive\\directoy\\xxxxx.txt");
System.out.println( "exists:" + file.exists() );
System.out.println( "is file:" + file.isFile() );
System.out.println( "can read:" + file.canRead() );
System.out.println( "can execute:" + file.canExecute() );
System.out.println( "can write:" + file.canWrite() );
System.out.println( "is hidden:" + file.isHidden() );
System.out.println("change it to be unreadable, and it works? " + file.setReadable(false));
System.out.println( "can read:" + file.canRead() );
System.out.println("change it to be writable, and it works? " + file.setWritable(true));
System.out.println( "can write:" + file.canWrite() );
FileInputStream fileInputStream = new FileInputStream(file);
fileInputStream.read();
fileInputStream.close();
}
}
I got:
exists:true
is file:true
can read:true
can execute:true
can write:false
is hidden:true
change it to be unreadable, and it works? false
can read:true
change it to be writable, and it works? true
can write:true
And now I can read this file, edit it and save it. Before changing the permission I was asked to Save As.. when saving.
Note that the file is readable, and setReadable(false) returns false, and the file is still readble. JavaDoc says here that setReadable() return false when user haven't permission to change the access premission, or when readable is already false, and the underlying system doesn't have implementation for this. Debugging into Java API doesn't provide much info, because the implementation are marked native and cannot see more. But I have the permission to change the writability, so that's something I don't understand.
But also note that there are more attributes out there that are not supported by java.util.File, like setHidden(). Maybe you can check other pacakges in java.security, like AccessController?

Related

Getting a specific version of an image with Jib (Maven, Docker, testcontainers)

I'm trying to understand a comment that a colleague made. We're using testcontainers to create a fixture:
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.utility.DockerImageName;
public class SalesforceFixture extends GenericContainer<SalesforceFixture> {
private static final String APPLICATION_NAME = "salesforce-emulator";
public SalesforceFixture() {
// super(ImageResolver.resolve(APPLICATION_NAME));
super(DockerImageName.parse("gcr.io/ad-selfserve/salesforce-emulator:latest"));
...
}
...
The commented code is what it used to be. The next line is my colleague's suggestion. And on that line he commented:
This is the part I don't know. The [ImageResolver] gets the specific version of the emulator, rather than the latest. You need a docker-info file for that though, which jib doesn't automatically generate (but I think it can).
This is what I know or have figured so far:
SalesforceFixture is a class that will be used by other projects to write tests. It spins up a container in Docker, running a service that emulates the real service's API. It's like a local version of the service that behaves enough like the real thing that if one writes code and tests using the fixture, it should work the same in production. (This is where my knowledge ends.)
I looked into ImageResolver—it seems to be a class we wrote that searches a filesystem for something:
public static String resolve(String applicationName, File... roots) {
Stream<File> searchPaths = Arrays.stream(roots).flatMap((value) -> {
return Stream.of(new File(value, "../" + applicationName), new File(value, applicationName));
});
Optional<File> buildFile = searchPaths.flatMap((searchFile) -> {
if (searchFile.exists()) {
File imageFile = new File(searchFile + File.separator + "/target/docker/image-name");
if (imageFile.exists()) {
return Stream.of(imageFile);
}
}
return Stream.empty();
}).findAny();
InputStream build = (InputStream)buildFile.map(ImageResolver::fileStream).orElseGet(() -> {
return searchClasspath(applicationName);
});
if (build != null) {
try {
return IOUtils.toString(build, Charset.defaultCharset()).trim();
} catch (IOException var6) {
throw new RuntimeException("An exception has occurred while reading build file", var6);
}
} else {
throw new RuntimeException("Could not resolve target image for application: " + applicationName);
}
}
But I'm confused. What filesystem? Like, what is the present working directory? My local computer, wherever I ran the Java program from? Or is this from within some container? (I don't think so.) Or maybe the directory structure inside a .jar file? Or somewhere in gcr.io?
What does he mean about a "specific version number" vs. "latest"? I mean, when I build this project, whatever it built is all I have. Isn't that equivalent to "latest"? In what case would an older version of an image be present? (That's what made me think of gcr.io.)
Or, does he mean, that in the project using this project's image, one will not be able to specify a version via Maven/pom.xml—it will always spin up the latest.
Sorry this is long, just trying to "show my work." Any hints welcome. I'll keep looking.
I can't comment on specifics of your own internal implementations, but ImageResolver seems to work on your local filesystem, e.g. it looks into your target/ directory and also touches the classpath. I can imagine this code was just written for resolving an actual image name (not an image), since it also returns a String.
Regarding latest, using a latest tag for a Docker image is generally considered an anti-pattern, so likely your colleague is commenting about this. Here is a random article from the web explaining some of the issues with latest tag:
https://vsupalov.com/docker-latest-tag/
Besides, I don't understand why you ask these questions which are very specific to your project here on SO rather than asking your colleague.

Using jna lib to write to registry throws either access is denied or System cannot find the file specified(lib not loaded)

So i have application that needs to use JNA lib to write to registry, when i want to run it from netbeans i get Access is denied i guess i dont have permissions to write with Advapi32Util.registrySetIntValue
So i created batch file that run my application with administrator privileges and did same thing now i got The system cannot find the file specified when i get to Advapi32Util.registrySetIntValue
To see better here is a screen:
On the left you see my netbeans execution and on the right batc hfile execution with admin privileges.I do have lib folder filled with same libraries , When you look at console of command promt you see VALUE READ:-1 , that means i cant even read with the library jna.platform.win32 Why is my library not loaded?
I use jnativehook that is in the folder lib and that one is loaded no problem , netbeans execution also loaded jna.platform.win32 as you can see i coud read value 1 instead of no value has been read -1
I have no idea what is happening there must be something wrong, both libraries are in the same folder, same imports its exactly the same .jar file beign coppied to test folder for jars
So question , why i get this result? Why is my lib not loaded when i can load library jnativehook that is in the same folder and use it normally bud jna throws error?Dont forget that everything loads well in netbeans , i just lack necessery privileges to run writing to registry(i can read from registry with no problem).
Additional not so important code information:
Imports:
import com.sun.jna.platform.win32.Advapi32Util;
import com.sun.jna.platform.win32.WinReg;
import org.jnativehook.GlobalScreen;
import org.jnativehook.NativeHookException;
Reading method:
public static final int readFromLEProperties(String guid) {
System.out.println("GUID IS:" + guid);
try {
int valueInReg = Advapi32Util.registryGetIntValue(
WinReg.HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\MMDevices\\"
+ "Audio\\Render\\" + guid + "\\FxProperties", "{E0A941A0-88A2-4df5-8D6B-DD20BB06E8FB},4");
System.out.printf("LE Value: %s\n", valueInReg);
return valueInReg;
} catch (Exception e) {
System.out.println("Coudnt read value from properties path:" + "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\MMDevices\\"
+ "Audio\\Render\\" + guid + "\\FxProperties NAME:" + "{E0A941A0-88A2-4df5-8D6B-DD20BB06E8FB},4");
}
return -1;
}
And writing method:
public static final void writeToLEProperties(String guid, boolean activateLE) {
try{
Advapi32Util.registrySetIntValue(WinReg.HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\MMDevices\\"
+ "Audio\\Render\\" + guid + "\\FxProperties", "{E0A941A0-88A2-4df5-8D6B-DD20BB06E8FB},4", (activateLE) ? 1 : 0);
}catch(Exception e){
System.out.println("Error: AD");
}
}

Trouble with ProcessBuilder

Following code opens status very fine in notepad:
import java.util.*;
class test
{
public static void main(String args[])
{
try{
ProcessBuilder pb=new ProcessBuilder("notepad","F:/status");
pb.start();
}catch(Exception e)
{
System.out.println(e);
}
}
}
Following code does'not play the song:
import java.util.*;
class test
{
public static void main(String args[])
{
try{
ProcessBuilder pb=new ProcessBuilder("C:/Program Files (x86)/VideoLAN/VLC/vlc","D:/02 Tu Jaane Na");
pb.start();
}catch(Exception e)
{
System.out.println(e);
}
}
}
I think that the problem is that you're ignoring the fact that the files you're trying to open have filename extensions.
Windows Explorer doesn't display file extensions by default - that is probably why you are not aware of their existence.
The reason why notepad worked in your first example is that notepad automatically adds .txt extension to its filename parameter in case you didn't provide one yourself. So in reality the file that is being open is not status but status.txt.
VLC doesn't have this "advanced" functionality because there's no specific filename extension it is designed to work with.
So you will need to look up the dir command output and add the full file name as a parameter.
If that was the real issue - you might want to modify your Windows Explorer settings for it to display file extensions:
or, which is better, switch to a more programmer-friendly OS :)
For 1.6+ code, use Desktop.open(File) instead.
Of course, the sensible thing to do immediately before calling that is to check File.exists().
OTOH, Desktop.open(File) throws a slew of handy exceptions, including:
NullPointerException - if file is null
IllegalArgumentException - if the specified file doesn't exist
UnsupportedOperationException - if the current platform does not support the Desktop.Action.OPEN action
IOException - if the specified file has no associated application or the associated application fails to be launched
Properly handled, the exception would indicate the immediate problem.
As an aside, the Desktop class is designed to be cross-platform, and will handle any file type for which an association is defined. In that sense it is a lot more useful for something like this, than trying to use a Process.

java.awt.Desktop.open doesn’t work with PDF files?

It looks like I cannot use Desktop.open() on PDF files regardless of location. Here's a small test program:
package com.example.bugs;
import java.awt.Desktop;
import java.io.File;
import java.io.IOException;
public class DesktopOpenBug {
static public void main(String[] args)
{
try {
Desktop desktop = null;
// Before more Desktop API is used, first check
// whether the API is supported by this particular
// virtual machine (VM) on this particular host.
if (Desktop.isDesktopSupported()) {
desktop = Desktop.getDesktop();
for (String path : args)
{
File file = new File(path);
System.out.println("Opening "+file);
desktop.open(file);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
If I run DesktopOpenBug with arguments c:\tmp\zz1.txt c:\tmp\zz.xml c:\tmp\ss.pdf (3 files I happen to have lying around) I get this result: (the .txt and .xml files open up fine)
Opening c:\tmp\zz1.txt
Opening c:\tmp\zz.xml
Opening c:\tmp\ss.pdf
java.io.IOException: Failed to open file:/c:/tmp/ss.pdf. Error message:
The parameter is incorrect.
at sun.awt.windows.WDesktopPeer.ShellExecute(Unknown Source)
at sun.awt.windows.WDesktopPeer.open(Unknown Source)
at java.awt.Desktop.open(Unknown Source)
at com.example.bugs.DesktopOpenBug.main(DesktopOpenBug.java:21)
What the heck is going on? I'm running WinXP, I can type "c:\tmp\ss.pdf" at the command prompt and it opens up just fine.
edit: if this is an example of Sun Java bug #6764271 please help by voting for it. What a pain. >:(
I never knew about this Desktop command, untill recently through this post:
would Java's Runtime.getRuntime().exec() run on windows 7?
Previously i have been using:
Runtime.getRuntime().exec("rundll32 SHELL32.DLL,ShellExec_RunDLL "+ myfile);
And it has always worked for me. If your method does not work, may be you can think about try this command.
If you switch the order of your arugments does that cause one of the other files to get that same error. I wonder if you need to trim the end of the path before calling the File constructor.
umm...yeah ignore that... check the documentation of Desktop.open. open throws an IO exception "if the specified file has no associated application or the associated application fails to be launched " ... also from the top of the page... "The mechanism of registereing, accessing, and launching the associated application is platform-dependent. "
code for the Desktop class: http://fuseyism.com/classpath/doc/java/awt/Desktop-source.html
The open method calls DesktopPeer.open.
DesktopPeer source: http://www.jdocs.com/javase/7.b12/java/awt/peer/DesktopPeer.html
DesktopPeer is implementation specific.
Here is source for a Windows-specific implementation:
http://www.java2s.com/Open-Source/Java-Document/6.0-JDK-Platform/windows/sun/awt/windows/WDesktopPeer.java.htm
open->ShellExecute->(Native)ShellExecute
Native ShellExecute is a wrapper for Win32 ShellExecute. Here is info on the function.
http://msdn.microsoft.com/en-us/library/bb762153(VS.85).aspx
My suggestion for a work around would be to write your own implmentation of the ShellExecute function. Here is source from someone who did it. http://www.heimetli.ch/shellexec.html

Launching java classes via windows drag-and-drop

I have a java class file with a main method. In Windows, I would like to be able to drag files onto a desktop icon/short/etc that would call supply the filenames to my main method. Basically, I want to allow users to drag-and-drop files at program execution instead of having type them on the command line.
Any thoughts?
To build on daub815's answer, in Windows, you can use a batch file to pass
arguments to another command. In this case, we'll use the java launcher to
launch your class with the main method.
I did a quick Google search on how to do write a batch file to take multiple arguments,
and found a page with a batch file to pass arguments to another command. Adapting from
the example, here is what you can do:
#ECHO OFF
:Loop
IF "%1" == "" GOTO Done
java YourClass %1
SHIFT
GOTO Loop
:Done
Save the above file as a batch file (with a ".bat" extension), and then you can drag-and-drop
files onto it, and it will be passed as arguments.
Also, you can call the batch file from the command line and pass arguments as well.
Edit: It appears that the batch file will not work with quoted arguments which contain spaces. Using a workaround presented in the site I've linked to will split the spaces contained in the quoted full path of the file into separate arguments, so that won't work either. If anyone has a good idea how to fix this, please either edit this entry, or post another answer. I will make this a community wiki.
PhiLho's answer works perfectly if you pack the classes in an executable JAR file (it's how you're meant to do it anyway) and make a .reg file that looks like the one below. Then just double-click that .reg file to merge it into the registry and you're good to go. This lets you both double-click a JAR file to run it, and starting it by Drag & Drop.
Do remember to change the path to where your Java executable is installed.
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\.jar]
#="jarfile"
[HKEY_CLASSES_ROOT\jarfile\DefaultIcon]
#="C:\\Java\\jdk1.7.0\\bin\\java.exe,1"
[HKEY_CLASSES_ROOT\jarfile\shell\open]
#="Run Java Program"
[HKEY_CLASSES_ROOT\jarfile\shell\open\command]
#="\"C:\\Java\\jdk1.7.0\\bin\\java.exe\" -jar \"%1\" %*"
[HKEY_CLASSES_ROOT\jarfile\shellex\DropHandler]
#="{86C86720-42A0-1069-A2E8-08002B30309D}"
OK, I made it work... The base knowledge is to use DropHandler UUID in the registry. I made a base setting, as follow:
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\.class]
#="JavaClass"
[HKEY_CLASSES_ROOT\JavaClass\DefaultIcon]
#="C:\\Java\\jdk1.6.0_05\\bin\\java.exe,1"
[HKEY_CLASSES_ROOT\JavaClass\shell\open]
#="Run Java class"
[HKEY_CLASSES_ROOT\JavaClass\shell\open\command]
#="\"C:\\Java\\jdk1.6.0_05\\bin\\java.exe\" \"%1\" %*"
[HKEY_CLASSES_ROOT\JavaClass\shellex\DropHandler]
#="{86C86720-42A0-1069-A2E8-08002B30309D}"
and... it didn't work!
I just forgot that java.exe wants a class name, not a file name! But I see no way to do that in the registry.
Fortunately, there is a workaround, which still need a script file if we want to be generic, to work on any/all class files (with static main function, of course!). Not batch, I avoid them when I can. I chose to use WSH, as it should be available on any modern Windows system. I also chose to make a JS script, it could have been a VB script as well.
So I made the following script (LaunchJavaClass.js):
if (WScript.Arguments.count() == 0)
{
WScript.StdOut.Write("No parameters");
WScript.Quit(1);
}
var className = WScript.Arguments.Item(0);
//~ WScript.StdOut.Write(className + "\n");
var m = className.match(/^(.*)\\(.+?)\.class$/);
if (m == null)
{
WScript.StdOut.Write("Not a class file");
WScript.Quit(1);
}
var classPath = m[1];
className = m[2];
//~ WScript.StdOut.Write(classPath + " >>> " + className + "\n");
var params = new Array();
for (i = 1; i < WScript.Arguments.count(); i++)
{
params[params.length] = WScript.Arguments.Item(i);
}
var cmd = "cmd /c cd /D " + classPath +
" & C:/Java/jdk1.6.0_05/bin/java.exe " +
className + " " + params.join(" ");
//~ WScript.StdOut.Write(cmd + "\n");
var shell = WScript.CreateObject("WScript.Shell");
//~ var exec = shell.Exec(cmd); // Can be used to get stdout
shell.Run(cmd, 0);
I left some output, not useful in this context, but usable for debugging (run with cscript).
Of course, the path to the JRE must be adjusted.
And I changed the command in the registry, as follow:
[HKEY_CLASSES_ROOT\JavaClass\shell\open\command]
#="\wscript -b "D:\\_PhiLhoSoft\\WSH\\LaunchJavaClass.js\" %1 %*"
Of course, adjust path, and keep the above other lines.
Now, if I drag'n'drop some files to a .class file, it gets the short file paths as arguments of the main() function.
import java.io.*;
class TestDnD
{
public static void main(String[] args)
{
Writer output = null;
try
{
output = new BufferedWriter(new FileWriter(new File("LogFile.txt")));
for (String arg : args)
{
output.write(arg + "\n");
}
}
catch (IOException ioe)
{
ioe.printStackTrace();
return;
}
finally
{
try { output.close(); } catch (IOException e) {}
}
}
}
I think the first version of the .reg file can be used for something else, eg. to drag'n'drop on .jar files (adapting it, of course).
This technique has limited use: we rarely make one-class programs in Java! But it looked like a good and interesting challenge, so I didn't resist to solve it. Note: you can add stuff like -Djava.ext.dirs="some path;another path" if you ever need to use external libraries (in jar files).
Adding onto Adiel A. If you create a batch file, which launches your a Java window using Swing. You would have the user drop the files onto that window. You could then be able to root through those dropped files.
So there's no way to have windows itself pass the args into main() via drag and drop?

Categories

Resources