I want to change the creation time of a directory in java (Linux).
Files are no problem:
Files.createFile(file.toPath());
Files.setAttribute(file.toPath(), "basic:lastModifiedTime", FileTime.fromMillis(creationDate), NOFOLLOW_LINKS);
Files.setAttribute(file.toPath(), "basic:creationTime", FileTime.fromMillis(creationDate), NOFOLLOW_LINKS);
That works.
But if I try to set this by directory creation nothing happens:
Files.createDirectory(file.toPath());
Files.setAttribute(file.toPath(), "basic:lastModifiedTime", FileTime.fromMillis(creationDate), NOFOLLOW_LINKS);
Files.setAttribute(file.toPath(), "basic:creationTime", FileTime.fromMillis(creationDate), NOFOLLOW_LINKS);
What could be the problem or is another approach the better way? (I use JDK 8)
Related
Little background for context:
The application I support allows third parties to develop plugins that can leverage some of our functionality. We hand them our "externalAPI.jar"; they put it in their project, implement some interfaces, and build their own APK. We find the would-be plugin by asking the package manager for all installed applications and see if each has a "pluginclass.xml" in the assets directory. If it has that XML file, we anticipate its contents being the canonical path of a class that implements our ExternalPluginVX interface, and using a new PathClassLoader(ApplicationInfo.sourceDir, this.getClass().getClassLoader()), we load the class, create a new instance, and start using it.
The problem:
Sometimes third parties will put
compile files ("./libs/externalAPI.jar")
in their gradle files instead of the correct syntax:
provided files ("./libs/externalAPI.jar")
The result of course being things don't work properly. Sometimes they almost work, but then have unpredictability in their behavior - usually involving vicious crashes. Notably, since their APK is well-formed in its own right, and the XML file is there, we'll see the plugin, load the target class successfully, instantiate it successfully, and things go haywire from there when they try and reference back to us.
The question:
Is there a way for my application to check at runtime if the other application compiled our API classes into their APK instead of using provided files like they should have?
A viable solution is to use a DexFile.
Since I already have the ApplicationInfo.sourceDir, I can just construct a DexFile and iterate through its contents.
//this variable's value assigned by iterating through context.getPackageManager().getInstalledApplications(0)
ApplicationInfo pkg;
String interfaceTheyShouldntHave = ExternalPluginVX.class.getCanonicalName(); //"com.project.external.ExternalPluginVX"
DexFile dexFile = new DexFile(pkg.sourceDir);
Enumeration<String> entries = dexFile.entries();
while(entries.hasMoreElements()){
String entry = entries.nextElement();
if(entry.equals(interfaceTheyShouldntHave)){
Toast.makeText(ctxt, "Plugin \"" + pluginName + "\" could not be loaded. Please use 'provided files' instead of 'compile files'", Toast.LENGTH_LONG).show();
return;
}
}
I can't seem to figure out why I can't create a directory from a Java program FOR A SPECIFIC LOCATION.
The specifics are - the folder of my computer is shared on the network.
Code:
File xmlDirectory = new File(sXMLOutputPath);
/*
* TODO: if multiple threads arive at the 1st if, if will evaluate to true,
* then 1st thread would create directory, and the 2nd being .01 sec later, will fail
* to create directory and have exception
* SOLUTION: Provide additional if exists, so that 2nd thread will recognize that it
* was created.
*/
if (!xmlDirectory.exists()){
if (!xmlDirectory.mkdirs()){
if (!xmlDirectory.exists()){
throw new BillAdminException("Failed to create xml directory: " +
sXMLOutputPath);
}
}
}
This is a server side code
In summary - if I share my foder C:\folder\etc - and pass it as JVM options to the program, the server side program "appends" \xml\333\333.xml" to it, and is supposed to create that xml file on MY PC. First it creates a structire C:\folder\etc\xml\333\, and then it creates 333.xml. It fails creating C:\folder\etc\xml\333 if the C:\folder\etc is passed as shared location in the form "\myMachine\etc", but works OK if I make that structure on some other machine "\OtherMachine\etc". If I pass it as "C:\folder\etc" (absolute, not shared form) it will work fine, creating directory and file on the server machine where code is executed. I need it created on my machine (client). What am I doing wrong when sharing folder.
P.S - this functionality worked about 2 month ago. However, the folder properties could have been tempered with since then. Not the java code, though.
P.S 2: this is not the only shared folder I pass from JVM options. There are 2 others, but used for reading (not creating subfolders)
Thanks, for help
P.S 3: The error I get is:
Failed to create xml directory: \\myMachine\etc\xml/333/
What I smell fishy, is that the slash before "333" is reversed. But, there were no changes in the code, so the same would have been happening before.
I tried in a lot of ways to clone a repo with jGit (it works).
Then, I write some archive in the repository, and tried to add all (a git add *, git add -A or something like it).. but it don't work. The files simple are not added to the staging area.
My code is like this:
FileRepositoryBuilder builder = new FileRepositoryBuilder();
Repository repository = builder.setGitDir(new File(folder))
.readEnvironment().findGitDir().setup().build();
CloneCommand clone = Git.cloneRepository();
clone.setBare(false).setCloneAllBranches(true);
clone.setDirectory(f).setURI("git#192.168.2.43:test.git");
try {
clone.call();
} catch (GitAPIException e) {
e.printStackTrace();
}
Files.write("testing it...", new File(folder + "/test2.txt"),
Charsets.UTF_8);
Git g = new Git(repository);
g.add().addFilepattern("*").call();
What am I doing wrong?
Thanks.
Exception while trying what with addFilePattern("."):
Exception in thread "main" org.eclipse.jgit.errors.NoWorkTreeException: Bare Repository has neither a working tree, nor an index
at org.eclipse.jgit.lib.Repository.getIndexFile(Repository.java:850)
at org.eclipse.jgit.dircache.DirCache.lock(DirCache.java:264)
at org.eclipse.jgit.lib.Repository.lockDirCache(Repository.java:906)
at org.eclipse.jgit.api.AddCommand.call(AddCommand.java:138)
at net.ciphersec.git.GitTests.main(GitTests.java:110)
One easy way to debug this is to look at the tests of the AddCommand in the JGit repo: AddCommandTest.java
You will see that in order to add all files the pattern "*" is never used, but "." is.
And it is used in the test function named... testAddWholeRepo()(!)
git.add().addFilepattern(".").call();
The Exception:
Exception in thread "main" org.eclipse.jgit.errors.NoWorkTreeException:
Bare Repository has neither a working tree, nor an index
is quite explicit: you need to add file in a non-bare repo.
See test method testCloneRepository() to compare with your own clone, and see if there is any difference.
I had a situation where I had to move a file f1 from the current directory to another directory called 'temp'. After moving the file, calling git.add().addFilePattern(".").call() acted in a weird way since git status gave the following result:
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: temp/f1.html
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
deleted: f1.html
It recognized that a new file temp/f1 was created but didn't detect that the file was deleted first. This was perhaps because moving the file can be seen as follows
Deleting/Cutting the file f1
Creating a folder called temp
Creating/Pasting the file f1
Then I came across the setUpdate(true) that looks for updates to files that are already being tracked and will not stage new files. (Check java-doc for more info)
So I had to change my code to two lines like so in order for git to recognize both files added and modified (which includes deletion):
git.add().addFilepattern(".").call();
git.add().setUpdate(true).addFilepattern(".").call();
git status now gives the expected result:
renamed: f1.hml -> temp/f1.html
It might be the wildcard, I just read the javadoc for the add command, looks like you send the name of a directory in order to add its contents not a wild card:
addFilepattern
public AddCommand addFilepattern(String filepattern)
Parameters: filepattern - File to add content from. Also a leading directory
name (e.g. dir to add dir/file1 and dir/file2) can be given to add all
files in the directory, recursively. Fileglobs (e.g. *.c) are not yet
supported.
Just to make a note about a problem i got where i was using File.separatorChar (Wich will give you either "/" or "\" depending on your OS) to change directory but actually jgit use only "/" and will do the job himself if you use separatorChar it will not work on windows.
Is there a way to convert JAR lib into JAR standalone?
I need to find a standalone java executable that convert PDF into TIFF and I've found these JARs: http://www.icefaces.org/JForum/posts/list/17504.page
Any ideas?
Easiest might be to create another Jar with a Main() entry point, and then just use the java.exe executable to run it:
e.g.
> java.exe -cp MyJarMain.jar;MyPDFJar.jar com.mydomain.MyMain myPDF.pdf
Where MyMain is a class with a Main static method.
You'll need something with a main entry point to pass in and interpret some command line arguments (myPDF.pdf in my made-up example)
You could do an assembly (are you using maven?) and make sure the Main-Class entry in the manifest.mf points to the main class.
Since there is no main-Method, you have to write one, or write a whole new class to call the class/method TiffConver.convertPDF .
The question is, how you're going to use it. From the command line, you need no executable jar. From the Gui, maybe you want to pass a file to be converted by drag and drop? Then you should take the parameter(s) passed to main as Input-PDF-Names (if they end in .pdf) and pass the names iteratively to TiffConverter, for "a.pdf b.pdf" =>
TiffConver.convertPDF ("a.pdf", "a.tiff");
TiffConver.convertPDF ("b.pdf", "b.tiff");
TiffCoverter will silently overwrite existing tiffs, so check that before or change the code there - this is clearly bad habit, and look out for more such things - I didn't.
/*
* Remove target file if exists
*/
File f = new File(tif);
if (f.exists()) {
f.delete();
}
Maybe you wan't to write a swing-wrapper, which let's you choose Files interactively to be converted. This would be a nice idee, if no filename is given.
If the user passes "a.pdf xy.tiff" you could rename the converted file to xy, as additional feature.
Without a main-class, however, a standalone jar would be magic.
However, building a native executale is almost always a bad idea. You loose portability, you don't profit from security- and performance improvements to the JVM or fixed bugs. For multiple programs you need always an independend bugfix, which you might have to manage yourself, if you don't have a package-management as most linux distros have.
after clearing some questions:
public static void main (String [] args) {
if (args.length == 1 && args[0].endsWith (".pdf")) {
String target = args[0].replaceAll (".pdf$", ".tif");
convertPDF (args[0], target);
}
}
This method you put into TiffConvert. It will allow you to convert a simple pdf-File, and generate a tif-File with the same basename but ending in .tif, silently overwriting an existing one of the same name.
I guess you now need to know how to start it?
I know. Heresy. But I'm in a bind. I have a lot of config files that use absolute path names, which creates an incompatibility between OS X and Windows. If I can get OS X (which I'm betting is the more flexible of the two) to recognize Q:/foo/bar/bim.properties as a valid absolute file name, it'll save me days of work spelunking through stack traces and config files.
In the end, I need this bit of Java test code to print "SUCCESS!" when it runs:
import java.io.*;
class DriveLetterTest {
static public void main(String... args) {
File f = new File("S:");
if (f.isDirectory()) {
System.out.println("SUCCESS!");
} else {
System.out.println("FAIL!");
}
}
}
Anyone know how this can be done?
UPDATE: Thanks for all the feedback, everyone. It's now obvious to me I really should have been clearer in my question.
Both the config files and the code that uses them belong to a third-party package I cannot change. (Well, I can change them, but that means incurring an ongoing maintenance load, which I want to avoid if at all possible.)
I'm in complete agreement with all of you who are appalled by this state of affairs. But the fact remains: I can't change the third-party code, and I really want to avoid forking the config files.
Short answer: No.
Long answer: For Java you should use System.getProperties(XXX).
Then you can load a Properties file or Configuration based on what you find in os.name.
Alternate Solution just strip off the S: when you read the existing configuration files on non-Windows machines and replace them with the appropriate things.
Opinion: Personally I would bite the bullet and deal with the technical debt now, fix all the configuration files at build time when the deployment for OSX is built and be done with it.
public class WhichOS
{
public static void main(final String[] args)
{
System.out.format("System.getProperty(\"os.name\") = %s\n", System.getProperty("os.name"));
System.out.format("System.getProperty(\"os.arch\") = %s\n", System.getProperty("os.arch"));
System.out.format("System.getProperty(\"os.version\") = %s\n", System.getProperty("os.version"));
}
}
the output on my iMac is:
System.getProperty("os.name") = Mac OS X
System.getProperty("os.arch") = x86_64
System.getProperty("os.version") = 10.6.4
Honestly, don't hard-code absolute paths in a program, even for a single-platform app. Do the correct thing.
The following is my wrong solution, saved to remind myself not to repeat giving a misdirected advice ... shame on me.
Just create a symbolic link named Q: just at the root directory / to / itself.
$ cd /
$ ln -s / Q:
$ ln -s / S:
You might need to use sudo. Then, at the start of your program, just chdir to /.
If you don't want Q: and S: to show up in the Finder, perform
$ /Developer/Tools/SetFile -P -a V Q:
$ /Developer/Tools/SetFile -P -a V S:
which set the invisible-to-the-Finder bit of the files.
The only way you can replace java.io.File is to replace that class in rt.jar.
I don't recommend that, but the best way to do this is to grab a bsd-port of the OpenJDK code, make necessary changes, build it and redistribute the binary with your project. Write a shell script to use your own java binary and not the built-in one.
PS. Just change your config files! Practice your regex skills and save yourself a lot of time.
If you are not willing to change your config file per OS, what are they for in first place?
Every installation should have its own set of config files and use it accordingly.
But if you insist.. you just have to detect the OS version and if is not Windows, ignore the letter:
Something along the lines:
boolean isWindows = System.getProperty("os.name").toLowerCase()
.contains("windows");
String folder = "S:";
if (isWindows && folder.matches("\\w:")) {
folder = "/";
} else if (isWindows && folder.matches("\\w:.+")) {
folder = folder.substring(2);// ignoring the first two letters S:
}
You get the idea
Most likely you'd have to provide a different java.io.File implementation that can parse out the file paths correctly, maybe there's one someone already made.
The real solution is to put this kind of stuff (hard-coded file paths) in configuration files and not in the source code.
Just tested something out, and discovered something interesting: In Windows, if the current directory is on the same logical volume (i.e. root is the same drive letter), you can leave off the drive letter when using a path. So you could just trim off all those drive letters and colons and you should be fine as long as you aren't using paths to items on different disks.
Here's what I finally ended up doing:
I downloaded the source code for the java.io package, and tweaked the code for java.io.File to look for path names that start with a letter and a colon. If it finds one, it prepends "/Volumes/" to the path name, coughs a warning into System.err, then continues as normal.
I've added symlinks under /Volumes to the "drives" I need mapped, so I have:
/Volumes/S:
/Volumes/Q:
I put it into its own jar, and put that jar at the front of the classpath for this project only. This way, the hack affects only me, and only this project.
Net result: java.io.File sees a path like "S:/bling.properties", and then checks the OS. If the OS is OS X, it prepends "/Volumes/", and looks for a file in /Volumes/S:/bling.properties, which is fine, because it can just follow the symlink.
Yeah, it's ugly as hell. But it gets the job done for today.