I need to find a solution to be able to manipulate a zip / jar directly (without unpacking) and without using third-party libraries. However I can't seem to grasp how the FileSystem ties in with Path and URI.
The URI that I'm trying to copy to is jar:file://E:/Projects/ZipDir/dist/test_folder/test.zip!/test_file.txt
The exception I'm getting is:
FileSystemNotFoundException but that zip file definitely does exist.
Using Java 7, this is what I have so far:
...
ZipDirectory zip = new ZipDirectory("test_folder/test.zip");
Path copyTo = zip.getPath("/test_file.txt");
Path copyFrom = Paths.get("test_file.txt");
Files.copy(copyFrom, copyTo, StandardCopyOption.REPLACE_EXISTING);
...
//
import java.io.IOException;
import java.net.URI;
import java.nio.file.*;
import java.util.HashMap;
public class ZipDirectory {
private Path path;
private FileSystem fileSystem;
public ZipDirectory(String path){
this.path = Paths.get(Paths.get(path).toUri());
create();
}
private void create(){
HashMap<String, String> env = new HashMap<>();
env.put("create", "true");
try {
fileSystem = FileSystems.newFileSystem(path, null);
} catch (IOException ex) {
System.out.println(ex);
}
}
public Path getPath(String relativePath){
return Paths.get(URI.create("jar:file:/" + path.toUri().getPath() + "!" + fileSystem.getPath(relativePath)));
}
public Path getRoot(){
return Paths.get(URI.create(path.toUri().getPath() + "!/"));
}
public void close(){
try {
fileSystem.close();
} catch (IOException ex) {
System.err.println(ex);
}
fileSystem = null;
}
}
I never thought I'd answer my own question, but I've got it working:
Treating an archive like a directory with Java 7
Related
import java.io.File;
import java.io.IOException;
public class TestFile {
public static void main(String[] args) {
String separator = File.separator;
String filename = "myFile.txt";
String directory = "mydir1" + separator + "mydir2";
File f = new File(directory,filename);
if (f.exists()) {
System.out.print("filename:" + f.getAbsolutePath());
System.out.println("filesize:" + f.length());
} else {
f.getParentFile().getParentFile().mkdir();
try{
f.createNewFile();
}catch (IOException e) {
e.printStackTrace();
}
}
}
}
What I am trying to do is create file "myFile.txt" under the folder "mydir1", but the console says "the system cannot find the path specified", can someone tell me where did I do wrong? Thanks in advance.
It looks like you create only mydir1 but not mydir2.
I can suggest instead of
f.getParentFile().getParentFile().mkdir();
try something like:
f.getParentFile().mkdirs();
File.mkdirs will try to create all required parrent directories.
I am trying to list all directories and files within a certain directory, but while "D:\data" works, "D:\" doesn't. "D" is a secondary disk.
This is the exception:
Exception in thread "main" java.lang.NullPointerException
at java.util.Objects.requireNonNull(Objects.java:203)
at java.util.Arrays$ArrayList.<init>(Arrays.java:3813)
at java.util.Arrays.asList(Arrays.java:3800)
at project.1.scavenger.listf(scavenger.java:19)
at project.1.scavenger.listf(scavenger.java:30)
at project.1.scavenger.listf(scavenger.java:30)
at project.1.main(Project1.java:28)
Java Result: 1
BUILD SUCCESSFUL (total time: 0 seconds)
Code:
public static List<File> listf(String directoryName) throws IOException {
File directory = new File(directoryName);
List<File> resultList = new ArrayList<File>();
// get all the files from a directory
File[] fList = directory.listFiles();
resultList.addAll(Arrays.asList(fList));
for (File file : fList) {
if (file.isFile()) {
System.out.println(file.getAbsolutePath());
try {
System.out.println(scavenger.checkmime(file.getAbsolutePath()));
} catch (Exception ex) {
}
} else if (file.isDirectory()) {
resultList.addAll(listf(file.getAbsolutePath()));
}
}
// System.out.println(fList);
return resultList;
}
public static String checkmime(String fl) throws MalformedURLException, IOException {
File file = new File(fl);
String mimeType = file.toURL().openConnection().getContentType();
// System.out.println(mimeType);
return mimeType;
}
What's wrong with my code?
Removed error from your version
In your recursion you never ask for null values. Do it and it should run like this:
public static List<File> listf(String directoryName) throws IOException {
File directory = new File(directoryName);
List<File> resultList = new ArrayList<>();
// get all the files from a directory
File[] fList = directory.listFiles();
// this was missing
if (fList == null) {
return null;
}
resultList.addAll(Arrays.asList(fList));
for (File file : fList) {
if (file.isFile()) {
System.out.println(file.getAbsolutePath());
try {
System.out.println(checkmime(file.getAbsolutePath()));
} catch (Exception ex) {
}
} else if (file.isDirectory()) {
// ask here if it was null
List<File> files = listf(file.getAbsolutePath());
if (files != null) {
resultList.addAll(files);
}
}
}
//System.out.println(fList);
return resultList;
}
Why D:\data works and D:\ not
In every root of a drive in a Windows System is a hidden folder structure called $RECYCLE.BIN. In this folder windows stores for each user (sid) an own folder with deleted data (links to it). But a normal user is only allowed to get the first level and not the user folder (with sid value as name).
(German Windows: Papierkorb = Trash)
A maybe much better solution:
A maybe better way of doing such searchings in tree's is to create an Iterator over the directory tree (like a composite iterator). This solution also uses only Java NIO features, so the platform should be changeable (haven't tested!) to for ex. Linux.
DirectoryTreeIterator.java
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Iterator;
public class DirectoryTreeIterator implements Iterator<Path> {
private final Deque<Iterator<Path>> deque = new ArrayDeque<>();
public DirectoryTreeIterator(Iterator<Path> it) {
deque.push(it);
}
#Override
public boolean hasNext() {
if (deque.isEmpty()) {
return false;
} else {
Iterator<Path> it = deque.peek();
if (!it.hasNext()) {
deque.pop();
return hasNext();
} else {
return true;
}
}
}
#Override
public Path next() {
if (hasNext()) {
Iterator<Path> it = deque.peek();
Path p = it.next();
try {
// here is the magic recursive on only filtered paths
if (Files.isDirectory(p) && Files.isReadable(p) && !Files.isHidden(p)) {
deque.push(Files.newDirectoryStream(p).iterator());
}
} catch (IOException ex) {
throw new UncheckedIOException(ex);
}
return p;
} else {
return null;
}
}
}
Then you are able to use it like a charm:
FileLister.java
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Iterator;
public class FileLister {
public static void main(String[] args) throws IOException {
Path p = Paths.get("D:\\");
Iterator<Path> it = Files.newDirectoryStream(p).iterator();
DirectoryTreeIterator dti = new DirectoryTreeIterator(it);
while (dti.hasNext()) {
Path path = dti.next();
if (!Files.isDirectory(path)) {
String mime = Files.probeContentType(path);
System.out.println("Mime of File "
+ path.getFileName() + " is: " + mime);
}
}
}
}
If you add this code right beneath the directory.listFiles() call:
if (fList == null) {
System.out.println("Couldn't read files in directory: " + directoryName);
return resultList;
}
then you will get a message like this:
Couldn't read files in directory: G:\$RECYCLE.BIN\Windows SID
This Windows SIDs is security identifier for a user on your local machine.
So that means your current user has no permission to read this directory (it belongs to a different user) in the "recycle bin" directory, therefore listFiles() returns null instead of a String[].
You could keep the check and the message I've posted, or you can implement a different way to handle "unreadable" directories.
About why "D:\\" failed and "D:\\data" not:
"D:\\" contains the mentioned "$RECYCLE.BIN" directory with restricted access
"D:\\data" has no directory where your current user isn't allowed to access/read it, therefore listFiles() never returned null.
I have a very different situation to deal with. Something never seen before.
I have a codebase which is not a maven based project. It basically is set of Pig Script that are executed on Hadoop Cluster.
Now there is requirement to test these scripts using PigUnit, so I created a maven based project with all dependencies needed for the project.
Visually it looks like
user_mapper/
src/main/
user.pig
other.pig
test/
pom.xml
src/java/
/UserTest.java
/OtherTest.java
As you could see, test is a maven based project in itself.
What I need
In UserTest.java I want to refer to relative path of user.pig
How can I provide a relative path in UserTest.java?
Try the following code (internally uses commons-io jar)
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.logging.Level;
import java.util.logging.Logger;
public class FileReader {
Logger logger = Logger.getLogger(FileReader.class.getName());
static String webAppPath;
private static final boolean IS_WINDOWS = System.getProperty( "os.name" ).contains( "indow" );
private InputStream inputStream;
private static FileReader fileReader;
public String getAbsolutePath(Class appClass, String relativePath) {
try {
String parentPath = "";
if (StringUtils.isNotBlank(webAppPath)) {
parentPath = webAppPath;
} else {
parentPath = appClass.getProtectionDomain().getCodeSource().getLocation().getPath();
}
String osAppropriatePath = IS_WINDOWS ? parentPath.substring(1) : parentPath;
String absolutePath = osAppropriatePath + relativePath;
File file = new File(absolutePath);
if (!file.exists()) {
FileUtils.writeStringToFile(file, IOUtils.toString(readFile(relativePath), "UTF-8"));
}
return absolutePath;
} catch (IOException ioe) {
logger.log(Level.SEVERE, null, ioe);
return relativePath;
}
}
public void closeFileReader() {
synchronized (this) {
try {
inputStream.close();
} catch (IOException ex) {
Logger.getLogger(FileReader.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
private FileReader() {
}
public static FileReader getInstance() {
return new FileReader();
}
public static String getWebAppPath() {
return webAppPath;
}
public static void setWebAppPath(String webAppPath) {
FileReader.webAppPath = webAppPath;
}
}
And call the class to get the relative path as follows
FileReader.getInstance().getAbsolutePath(user.pig, "user.pig");
I solved this issue by using java.io.File as
final String filePath = new File("../src/user.pig").getAbsolutePath();
I am trying to get the i18n properties file out of my BuildPath. If you are trying to get the PropertiesFile the ResourceBundle.getBundlewill throw a java.util.MissingResourceException. Is there a method to load i18n files from outside the BuildPathbut still has the comfort of detecting your locale?
EDIT:
Here is the solution I was able to create with the Help of Paweł Dyda. Maybe somebody will need it. Probably there could be some improvements made, but it works ;)
import java.io.File;
import java.io.FileNotFoundException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.List;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle.Control;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.io.FilenameUtils;
import org.apache.log4j.Logger;
public class GlobalConfigurationProvider {
Logger logger = Logger.getLogger(GlobalConfigurationProvider.class);
private static GlobalConfigurationProvider instance;
PropertiesConfiguration i18n;
private GlobalConfigurationProvider() {
String path = GlobalConfigurationProvider.class.getProtectionDomain().getCodeSource().getLocation().getPath();
String decodedPath = "";
try {
decodedPath = URLDecoder.decode(path, "UTF-8");
// This ugly thing is needed to get the correct
// Path
File f = new File(decodedPath);
f = f.getParentFile().getParentFile();
decodedPath = f.getAbsolutePath();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
this.logger.error("Failed to decode the Jar path", e);
}
this.logger.debug("The Path of the jar is: " + decodedPath);
String configFolder = FilenameUtils.concat(decodedPath, "cfg");
String i18nFolder = FilenameUtils.concat(configFolder, "i18n");
File i18nFile = null;
try {
i18nFile = this.getFileForLocation(new File(i18nFolder), Locale.getDefault());
} catch (FileNotFoundException e) {
e.printStackTrace();
this.logger.error("Can't find the LocaleFile", e);
}
if (!i18nFile.exists()) {
// If this can't be found something is wrong
i18nFile = new File(i18nFolder, "eng.i18n");
if (!i18nFile.exists()) {
this.logger.error("Can't find the i18n File at the Location: " + i18nFile.getAbsolutePath());
}
}
this.logger.debug("The Path to the i18n File is: " + i18nFile);
try {
this.i18n = new PropertiesConfiguration(i18nFile);
} catch (ConfigurationException e) {
this.logger.error("Couldn't Initialize the i18nPropertiesFile", e);
}
}
private File getFileForLocation(File i18nFolder, Locale locale) throws FileNotFoundException {
Control control = Control.getControl(Control.FORMAT_DEFAULT);
List<Locale> locales = control.getCandidateLocales(this.getBaseName(), locale);
File f = null;
for (Locale l : locales) {
String i18nBundleName = control.toBundleName(this.getBaseName(), l);
String i18nFileName = control.toResourceName(i18nBundleName, "properties");
f = new File(i18nFolder, i18nFileName);
this.logger.debug("Looking for the i18n File at: " + f);
if (f.exists()) {
return f;
}
}
// Last try for a File that should exist
if (!locale.equals(Locale.US)) {
return this.getFileForLocation(i18nFolder, Locale.US);
}
throw new FileNotFoundException("Can't find any i18n Files in the Folder " + i18nFolder.getAbsolutePath());
}
private String getBaseName() {
// TODO: Get this from the Settings later
return "messages";
}
public static GlobalConfigurationProvider getInstance() {
if (GlobalConfigurationProvider.instance == null) {
GlobalConfigurationProvider.instance = new GlobalConfigurationProvider();
}
return GlobalConfigurationProvider.instance;
}
public String getI18nString(String key) {
try {
return this.i18n.getString(key);
} catch (MissingResourceException e) {
return '!' + key + '!';
}
}
}
Of course there are methods to do that. Anyway, I believe your problem is the wrong path to the resource you are trying to load.
Nonetheless, for sure you are looking the way to use Locale fall-back mechanism to load very specific resource. It can be done. You may want to take a look at ResourceBundle.Control class. For example you can get the list of fall-back locales:
Control control = Control.getControl(Control.FORMAT_DEFAULT);
List<Locale> locales = control.getCandidateLocales("messages",
Locale.forLanguageTag("zh-TW"));
From there, you can actually create names of the resource files you are looking for:
for (Locale locale : locales) {
String bundleName = control.toBundleName("messages", locale);
String resourceName = control.toResourceName(bundleName, "properties");
// break if resource under given name exist
}
Then, you need to load the resource somehow - you may want to use ClassLoader's getResourceAsStream(String) to open the InputStream for you. The last step could be actually use the stream as an input to PropertyResourceBundle:
ResourceBundle bundle = new PropertyResourceBundle(inputStream);
You can alternatively pass a Reader rather than InputStream, which has at least one advantage - you may actually allow properties file to be encoded in UTF-8, rather than regular ISO8859-1.
I need to write a web-aplication that connects to database and reads connections properties from .properties file.
I use Maven and I want to write some unit test for my app.
To test classes that works with database I put my .properties file to src/main/resources and to test/main/resources and wrote the following code for reading properties:
package util;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import org.apache.log4j.Logger;
/**
*
* #author proger
*/
public class ProgrammSettings {
private static Logger logger = Logger.getRootLogger();
private static String propertiesPath = "program.settings.properties";
private static Properties props;
public static String getProperty(String key) {
if (props == null)
loadProperties();
return props.getProperty(key);
}
public static void setResourcesFilePath(String path) {
propertiesPath = path;
}
private static void loadProperties() {
InputStream propOut = null;
try {
//URL url = ClassLoader.getSystemResource(propertiesPath);
propOut = ClassLoader.getSystemClassLoader().getResourceAsStream(propertiesPath); //new FileInputStream(url.getFile());
props = new Properties();
props.load(propOut);
} catch (Exception ex) {
//Logger.getLogger(ProgrammSettings.class.getName()).log(Level.SEVERE, null, ex);
String errorMessage = "Error during properties file reading. Path to file: '" +
propertiesPath + "'";
logger.error(errorMessage, ex);
throw new RuntimeException(errorMessage, ex);
} finally {
try {
if (propOut != null)
propOut.close();
} catch (IOException ex) {
logger.error("Error during closing stream from properties file", ex);
}
}
}
}
I wanted the same code worked both for tests and app classes, so I read properties file from classPath.
All my tests pass and my .properties file is packed into war by Maven. But when I run my App from Netbeans this I receive:
java.lang.NullPointerException
java.util.Properties$LineReader.readLine(Properties.java:435)
java.util.Properties.load0(Properties.java:354)
java.util.Properties.load(Properties.java:342)
util.ProgrammSettings.loadProperties(ProgrammSettings.java:49)
util.ProgrammSettings.getProperty(ProgrammSettings.java:33)
dataacess.AbstractDataMapper.loadDriver(AbstractDataMapper.java:58)
dataacess.AbstractDataMapper.<init>(AbstractDataMapper.java:41)
dataacess.UserMapper.<init>(UserMapper.java:25)
servlets.UserRegistration.processRequest(UserRegistration.java:86)
servlets.UserRegistration.doPost(UserRegistration.java:132)
javax.servlet.http.HttpServlet.service(HttpServlet.java:637)
javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
org.netbeans.modules.web.monitor.server.MonitorFilter.doFilter(MonitorFilter.java:393)
How can I fix it? Thank you in advance
Place program.settings.properties in webapps/appname/WEB-INF/classes
And you can use a simpler line ProgrammSettings.class.getResourceAsStream(..) (i.e. not the system classloader, but the current classloader)