Read from file system via FileSystem object - java

In order to list file contents of a specific directory on classpath I'm using the new FileSystem and Path features of Java 7. In one deployment the directory is stored on file system, directly. In another deployment it is stored into a JAR file.
My approach works fine with JAR files: I create a FileSystem object which refers to the JAR file and access the content via Path object.
...
URI dir = ...
String[] array = dir.toString().split("!");
try (final FileSystem fs = FileSystems.newFileSystem(URI.create(array[0]), new HashMap<String, Object>()))
{
final Path directory = fs.getPath(array[1]);
try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(directory))
{
...
Due to the dir object has following value, it works:
jar:file:/C:/Users/pax/.../Detector-1.0.jar!/org/.../destinationdir
But in the other environment the destination directory is stored on file system, directly. dir object contains the value:
file:/C:/Users/pax/.../destinationdir
FileSystems.newFileSystem(...) always throws following exception for / and file:/C:/Users/pax/.../destinationdir as URI:
java.lang.IllegalArgumentException: Path component should be '/'
at sun.nio.fs.WindowsFileSystemProvider.checkUri(WindowsFileSystemProvider.java:68)
How do you use FileSystem.newFileSystem for destinations on file system?
Is there a better approach in order to list the directories content independently from its specific kind of storage (file system or JAR file)?

Following question's resolution tackles the issue ("destination on file system" versus "destination in JAR file") by try-catch approach: NIO2: how to generically map a URI to a Path?
This utility method tries to obtain a correct Path instance. But there may occur a further problem: If this destination resource is contained by a JAR file (instead of file system) then you can only access the resource via its associated FileSystem instance which must not be closed! So, your helper method needs to return the Path object as well as the FileSystem instance (only required if it's not on file system directly). The invoker has to close the FileSystem object, manually:
public static PathReference getPath(final URI resPath) throws IOException
{
try
{
// first try getting a path via existing file systems
return new PathReference(Paths.get(resPath), null);
}
catch (final FileSystemNotFoundException e)
{
/*
* not directly on file system, so then it's somewhere else (e.g.:
* JAR)
*/
final Map<String, ?> env = Collections.emptyMap();
final FileSystem fs = FileSystems.newFileSystem(resPath, env);
return new PathReference(fs.provider().getPath(resPath), fs);
}
}
The wrapper class PathReference should implement AutoClosable so that it can be used in try block:
public class PathReference implements AutoCloseable
{
...
#Override
public void close() throws Exception
{
if (this.fileSystem != null)
this.fileSystem.close();
}
public Path getPath()
{
return this.path;
}
public FileSystem getFileSystem()
{
return this.fileSystem;
}
}
This makes the release of the FileSystem instance a bit more transparent:
...
try (final PathReference fileObj = SignatureUtils.getPath(file))
{
...
try (InputStream fileStream = Files.newInputStream(fileObj.getPath()))
{
...

Related

Not able to read resource files from src/test/resources in STS

I created folder src/test/resources/ in root project directory, and inside this I added a file in folder jsons as jsons/server_request.json.
Now I am trying to read this file by calling a the static function in CommonTestUtilityclass given as:
public class CommonTestUtility {
public static String getFileAsString(String fileName) throws IOException {
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
File file = new File(classLoader.getResource(fileName).getFile());
String content = new String(Files.readAllBytes(file.toPath()));
return content;
}
}
Now while calling this function as
class ServerTest {
#Test
void test_loadResource() {
String content = CommonTestUtility.getFileAsString("jsons/server_request.json");
}
}
, It's giving me the error as:
CommonTestUtility - Cannot invoke "java.net.URL.getFile()" because the return value of "java.lang.ClassLoader.getResource(String)" is null.
I tried to include the src/test/resources/ in the run configuration
of Junit ServerTest.java, but still it's not able to find out the
resource
How to resolve this issue?
https://mkyong.com/java/java-read-a-file-from-resources-folder/
This above link might be helpful.
The getResource() method return an URI you need to change
.getFile() function to. toURI().
Simple code
private File getFileFromResource(String fileName) throws URISyntaxException{
ClassLoader classLoader = getClass().getClassLoader();
URL resource = classLoader.getResource(fileName);
if (resource == null) {
throw new IllegalArgumentException("file not found! " + fileName);
} else {
// failed if files have whitespaces or special characters
//return new File(resource.getFile());
return new File(resource.toURI());
}
}
I recreated the same scenario you describe and your code works for me.
Could you double-check that your project looks like mine below? If so, I suspect it might be something with your environment.

How can you create a new zip file in Java and add a large directory of files to it?

I'm trying to add a directory of files to a zip. The directory is around 150 files large. A few, 5-75 files in, I keep getting a crash with the error message "The process cannot access the file because it is being used by another process."
I tried a delay which may be helping but is certainly not solving the bug.
Using code from:
Is it possible to create a NEW zip file using the java FileSystem?
final File folder = new File("C:/myDir/img");
for (final File fileEntry : folder.listFiles()) {
if (fileEntry.isDirectory()) {
continue;
}
else {
String filename = fileEntry.getName();
String toBeAddedName = "C:/myDir/img/" + filename;
Path toBeAdded = FileSystems.getDefault().getPath(toBeAddedName).toAbsolutePath();
createZip(zipLocation, toBeAdded, "./" + filename);
System.out.println("Added file " + ++count);
//Delay because 'file in use' bug
try { Thread.sleep(1000); } //1secs
catch (InterruptedException e) {}
}
}
public static void createZip(Path zipLocation, Path toBeAdded, String internalPath) throws Throwable {
Map<String, String> env = new HashMap<String, String>();
//Check if file exists.
env.put("create", String.valueOf(Files.notExists(zipLocation)));
//Use a zip filesystem URI
URI fileUri = zipLocation.toUri(); //Here
URI zipUri = new URI("jar:" + fileUri.getScheme(), fileUri.getPath(), null);
System.out.println(zipUri);
//URI uri = URI.create("jar:file:"+zipLocation); //Here creates the zip
//Try with resource
try (FileSystem zipfs = FileSystems.newFileSystem(zipUri, env)) {
//Create internal path in the zipfs
Path internalTargetPath = zipfs.getPath(internalPath);
//Create parent dir
Files.createDirectories(internalTargetPath.getParent());
//Copy a file into the zip file
Files.copy(toBeAdded, internalTargetPath, StandardCopyOption.REPLACE_EXISTING);
}
}
I can't promise this is the cause of your problem, but your code compresses files into a ZIP file in a strange, or at least inefficient, manner. Specifically, you're opening up a new FileSystem for each individual file you want to compress. I'm assuming you're doing it this way because that's what the Q&A you linked to does. However, that answer is only compressing one file whereas you want to compress multiple files at the same time. You should keep the FileSystem open for the entire duration of compressing your directory.
public static void compress(Path directory, int depth, Path zipArchiveFile) throws IOException {
var uri = URI.create("jar:" + zipArchiveFile.toUri());
var env = Map.of("create", Boolean.toString(Files.notExists(zipArchiveFile, NOFOLLOW_LINKS)));
try (var fs = FileSystems.newFileSystem(uri, env)) {
Files.walkFileTree(directory, Set.of(), depth, new SimpleFileVisitor<>() {
private final Path archiveRoot = fs.getRootDirectories().iterator().next();
#Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
// Don't include the directory itself
if (!directory.equals(dir)) {
Files.createDirectory(resolveDestination(dir));
}
return FileVisitResult.CONTINUE;
}
#Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
Files.copy(file, resolveDestination(file), REPLACE_EXISTING);
return FileVisitResult.CONTINUE;
}
private Path resolveDestination(Path path) {
/*
* Use Path#resolve(String) instead of Path#resolve(Path). I couldn't find where the
* documentation mentions this, but at least three implementations will throw a
* ProviderMismatchException if #resolve(Path) is invoked with a Path argument that
* belongs to a different provider (i.e. if the implementation types don't match).
*
* Note: Those three implementations, at least in OpenJDK 12.0.1, are the JRT, ZIP/JAR,
* and Windows file system providers (I don't have access to Linux's or Mac's provider
* source currently).
*/
return archiveRoot.resolve(directory.relativize(path).toString());
}
});
}
}
Note: The depth parameter is used in exactly the same way as maxDepth is in Files#walkFileTree.
Note: If you only ever care about the files in the directory itself (i.e. don't want to recursively traverse the file tree), then you can use Files#list(Path). Don't forget to close the Stream when finished with it.
It's possible that you opening and closing the FileSystem over and over is causing your problems, in which case the above should solve the issue.

Access public assets with Java in Play Framework

Is it possible to access Assets inside the Java code in Play Framework? How?
We access assets from the scala HTML templates this way:
<img src="#routes.Assets.versioned("images/myimage.png")" width="800" />
But I could not find any documentation nor code example to do it from inside the Java code. I just found a controllers.Assets class but it is unclear how to use it. If this is the class that has to be used, should it maybe be injected?
I finally found a way to access the public folder even from a production mode application.
In order to be accessible/copied in the distributed version, public folder need to be mapped that way in build.sbt:
import NativePackagerHelper._
mappings in Universal ++= directory("public")
The files are then accessible in the public folder in the distributed app in production form the Java code:
private static final String PUBLIC_IMAGE_DIRECTORY_RELATIVE_PATH = "public/images/";
static File getImageAsset(String relativePath) throws ResourceNotFoundException {
final String path = PUBLIC_IMAGE_DIRECTORY_RELATIVE_PATH + relativePath;
final File file = new File(path);
if (!file.exists()) {
throw new ResourceNotFoundException(String.format("Asset %s not found", path));
}
return file;
}
This post put me on the right way to find the solution: https://groups.google.com/forum/#!topic/play-framework/sVDoEtAzP-U
The assets normally are in the "public" folder, and I don't know how you want to use your image so I have used ImageIO .
File file = new File("./public/images/nice.png");
boolean exists = file.exists();
String absolutePath = file.getAbsolutePath();
try {
ImageInputStream input = ImageIO.read(file); //Use it
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("EX = "+exists+" - "+absolutePath);

Why I cannot read properties file using getResourceAsStream from WEB-INF/classes in WildFly? [duplicate]

This question already has answers here:
How to read properties file in web application? [duplicate]
(3 answers)
Closed 7 years ago.
My PC's operating system is Windows 7 64-bit.
I created a very simple Dynamic Web Project app in Eclipse:
I have a app.properties file in WEB-INF/classes directory with these properties:
DefaultMaximumBatchSize=1000
DAOFactory=MSSQLSERVER
I have a class AppProperties which reads the above file into a Properties object at startup using getResourceAsStream:
public class AppProperties {
private static final Properties APP_PROPERTIES;
static {
InputStream inputStream = null;
APP_PROPERTIES = new Properties();
try {
inputStream = AppProperties.class.getResourceAsStream("/WEB-INF/classes/app.properties");
System.out.println("AppProperties: inputStream=" + inputStream);
if (inputStream != null) {
APP_PROPERTIES.load(inputStream);
}
} catch (Exception e) {
System.out.println("AppProperties: Exception occured; e=" + e);
}
}
public static String getValue(String propertyName) {
if (propertyName == null || propertyName.equalsIgnoreCase(""))
return null;
else
return APP_PROPERTIES.getProperty(propertyName);
}
}
I have a listener class AppContextListener:
public class AppContextListener implements ServletContextListener {
public AppContextListener() {
}
public void contextInitialized(ServletContextEvent arg0) {
String defaultMaxBatchSize = AppProperties.getValue("DefaultMaximumBatchSize");
System.out.println("AppContextListener: contextInitialized(ServletContextEvent): defaultMaxBatchSize=" + defaultMaxBatchSize);
}
public void contextDestroyed(ServletContextEvent arg0) {
}
}
I deployed the app to JBoss 4.2.3, run the JBoss 4.2.3 and I get this output in server.log:
AppProperties: inputStream=java.io.FileInputStream#1adde645
AppContextListener: contextInitialized(ServletContextEvent): defaultMaxBatchSize=1000
Perfect.
I then deployed the same app to WildFly 8.2.1, run the WildFly 8.2.1 and I get this output in server.log:
AppProperties: inputStream=null
AppContextListener: contextInitialized(ServletContextEvent): defaultMaxBatchSize=null
What happened? What is the correct way to read properties file in WildFly from WEB-INF/classes directory?
Class.getResourceAsStream() looks for a resource in all of the directories and jars that constitute the classpath of the application.
So, if you start a java program with
java -cp foo;bar.jar com.baz.Main
And you use SomeClass.class.getResourceAsStream("/blabla/app.properties"), The classloader will look for the app.properties file under foo/blabla, and in the blabla directory of bar.jar.
Now, in a webapp, what constitutes the classpath of the webapp is
the directory WEB-INF/classes
all the jar files under WEB-INF/lib
So, if you call
AppProperties.class.getResourceAsStream("/WEB-INF/classes/app.properties")
the classloader will look for app.properties in
/WEB-INF/classes/WEB-INF/classes
<all the jar files of WEB-INF/lib>/WEB-INF/classes
The conclusion is that, to load an app.properties file located in WEB-INF/classes, what you need is
AppProperties.class.getResourceAsStream("app.properties")
JBoss shouldn't have worked.
Class.getResourceAsStream retrieves the resource from the classpath and the webapp root folder is not in the classpath.
The WEB-INF/classes folder is. Use getResourceAsStream("/app.properties"), and remember to close the stream:
private static final Properties APP_PROPERTIES = new Properties();
static {
try (InputStream inputStream = AppProperties.class.getResourceAsStream("/app.properties")) {
System.out.println("AppProperties: inputStream=" + inputStream);
if (inputStream != null)
APP_PROPERTIES.load(inputStream);
} catch (Exception e) {
System.out.println("AppProperties: Exception occured; e=" + e);
}
}
Now, if app.properties is always next to AppProperties.class, instead of at the root, make the name unqualified (remove the /). This will work even when your class is in a package (and it is in a package, right?).
Try
InputStream inputStream =
this.getClass().getClassLoader().getResourceAsStream("/my.properties");`

Reading Properties file inside conf folder

I have a properties file which is located under conf folder. conf folder is under the project root directory. I am using the following code.
public class PropertiesTest {
public static void main(String[] args) {
InputStream inputStream = PropertiesTest.class
.getResourceAsStream("/conf/sampleprop.conf");
Properties prop = new Properties();
try {
prop.load(inputStream);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(prop.getProperty("TEST"));
}
}
But I get nullpointer exception.
I have tried using
InputStream inputStream = PropertiesTest.class
.getResourceAsStream("./conf/sampleprop.conf");
and
InputStream inputStream = PropertiesTest.class
.getResourceAsStream("conf/sampleprop.conf");
But all result in nullpointer exception.
Can anyone please help.
Thanks in advance
Try to recover your working directory first:
String workingDir = System.getProperty("user.dir");
System.out.println("Current working dir: " + workingDir);
and then is simple:
Properties propertiesFile = new Properties();
propertiesFile.load(new FileInputStream(workingDir+ "/yourFilePath"));
String first= propertiesFile.getProperty("myprop.first");
Regards, fabio
The getResourceAsStream() method tries to locate and load the resource using the ClassLoader of the class it is called on. Ideally it can locate the files only the class folders .. Rather you could use FileInputStream with relative path.
EDIT
if the conf folder is under src, then you still be able to access with getResourceAsStream()
InputStream inputStream = Test.class
.getResourceAsStream("../conf/sampleprop.conf");
the path would be relative to the class from you invoke getRes.. method.
If not
try {
FileInputStream fis = new FileInputStream("conf/sampleprop.conf");
Properties prop = new Properties();
prop.load(fis);
System.out.println(prop.getProperty("TEST"));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
NOTE: this will only work if it is Stand alone application/in eclipse. This will not work if its web based (as the root will be Tomcat/bin, for eg)
I would suggest to copy the configuration file at designated place, then you can acess at ease. At certain extent 'System.getProperty("user.dir")' can be used if you are always copying the file 'tomcat` root or application root. But if the files to be used by external party, ideal to copy in a configurable folder (C:\appconf)
Your code works like a charm! But you might have to add the project root dir to your classpath.
If you work with Maven, place your configuration in src/main/resources/conf/sampleprop.conf
When invoking java directly add the project root dir with the java -classpath parameter. Something like:
java -classpath /my/classes/dir:/my/project/root/dir my.Main

Categories