Java 7 NIO.2 Paths to make path handling easy for you

12 12 2015

Java 7 introduced a big change in file and directory structure manipulation API. Now you have:

  • Path that represents locacation in a filesystem (filesystem can be Windows, *nix or a zip archive)
  • Files to deal with directory trees (walk directory tree, iterate over files in directory, filter files, perform recursive move, copy, delete)
  • WatchService to listen for the changes in file
  • File attributes access like size, permissions, modification time
  • Symbolic links detection and support
  • Asynchronous I/O operations (with Future or callback style) for processing big files without impact on main application Thread

Now its time for short demo of new Path features. See the demo source code on my GitHub: https://github.com/yacekmm/looksok/tree/PathsDemo/Java/Java7NIO.2

Creating a Path

See how to get the absolute path to the current working directory:

Path path = Paths.get("").toAbsolutePath();
System.out.println("Current working dir absolute path is: " + path);

Current absolute path is:

C:\Code\looksok\looksok\Java\Java7NIO.2

Path properties

On linux system you can analyze the path properties with some convenient methods:

path = Paths.get("/var/lib/jenkins/jobs/sampleJob");
System.out.println("\nSample linux path that will be analyzed: " + path.toAbsolutePath());
System.out.println("Number of segments in path: " + path.getNameCount());
System.out.println("Parent in path: " + path.getParent());
System.out.println("Root on this filesystem: " + path.getRoot());
System.out.println("Subpath (2 elements up): " + path.subpath(path.getNameCount() - 2, path.getNameCount()));

Notice that I’ve created linux Path on Windows machine. This is possible because path is an abstract concept that may point to non existing resource. Because it can be a path to resource that you are just about to create in that Path. Meaning that the path existence will be verified already when you want to do something in this location.

The properties check result is:

Sample linux path that will be analyzed: C:\var\lib\jenkins\jobs\sampleJob
Number of segments in path: 5
Parent in path: \var\lib\jenkins\jobs
Root on this filesystem: \
Subpath (2 elements up): jobs\sampleJob

Symbolic link path finding

When you work with paths containing Symbolic links (kind of shortcuts to the target file on linux OS) you need to know whether it is a file or symlink and get the real location of the file behind the link. Do it with:

path = Paths.get("/var/log/logFile");
System.out.println("Real symlink path: " + path.toRealPath());

getRealPath() throws IOException if Path does not exist. You can also getRealPath(LinkOptions.NOFOLLOW_LINKS) if you want a symlink file path, not the element behind it.

Converting paths

In NIO.2 you can easily join Paths, create a Path between two other Paths, and compare Paths against each other. This is how you join paths

Path currentDir = Paths.get("");
Path relativeConfigPath = Paths.get("conf/app.properties");
Path propertiesFilesPath = currentDir.resolve(relativeConfigPath);

The resolved path is:

C:\Code\looksok\looksok\Java\Java7NIO.2\conf\app.properties

Path between two other Paths

This is how to get the relative path between two other Paths:

Path logsDir = Paths.get("/var/log/myapp");
Path appDir = Paths.get("/var/lib/myapp");
Path relativePathToLogs = appDir.relativize(logsDir);

And the result is a relative path:

..\..\log\myapp

NIO.2 and existing Java’s File class

You can now refactor pre Java 7 code to use new constructs, however if you need to interact with old APIs using File class, be noticed that File has now toPath() method, as well as the Path having its toFile() equivalent:

File file = new File("./PathDemo.java");
Path pathFromFile = file.toPath();
file = pathFromFile.toFile();




Java 7: Try-With-Resources block that closes the resources for you

28 11 2015

Working with resources (like files stream, byte stream, network stream) in Java requires to close() the resource after you’re done. How often did you forget to close it in your finally block? Even if you didn’t – Java creators proved that 75% of close() implementations got bugs in it. Since Java 7 all resources you use can be automatically closed when try-catch block completes thanks to the new syntax.

Closing resources old-style

You needed to try to close the resource in finally block (and handle the exception if close failed). It looked like that:

InputStream is = null;
try{
	is = url.openStream();
	//read and process the stream
}catch(AllPossibleExceptions){
	//and handle them
}finally{
	try{
		if(is != null)
			is.close();
	}catch(IOException){
		//handle exception on closing the stream
	}
}

Quite a lot of repeatable exception handling and null-avoidance code.

Try With Resources (TWR) Syntax in Java 7

This is how you write resource processing code since Java 7 with improved try()-catch syntax:

try (InputStream is = url.openStream()){
	//read and process the stream
	//stream will be automatically closed for you
}catch(AllPossibleExceptions){
	//and handle them
}

Notice the new try() syntax where you initialize the resource. Stream that is declared there will be automatically closed when the try block execution ends. You can have more Streams initialized there, separated with semicolons.

Why the resource is being closed?

Because it implements AutoClosable interface – the new interface in Java 7. Class must implement that interface to appear in the try() clause. The AutoClosable is a superinterface of the Closable interface (both have only the close() method that closes the resource)

Ceveat

Be aware that nested resources will not be closed. Do not use syntax like that:

try ( ObjectInputStream in = new ObjectInputStream(new 
			FileInputStream("app.properties")) ) {
	//...
}

the issue here is that the inner resource (FileInputStream) will not be closed. If you need constructs like this, where one resource depends on another, then separate their initialization like that:

try ( FileInputStream fileStream = new FileInputStream("app.properties");
	ObjectInputStream in = new ObjectInputStream(fileStream) ) {
	//...
}

then the TWR will work its best for you.





Java 7 enhanced syntax for numeric literals

21 11 2015

Two convenient syntax improvements were implemented in Java 7 in this topic:

  • Numeric constants can now be expressed as binary literals
  • underscores can be used in integers as a separator for sake of readability

Binary literals

Before Java 7 to get an Integer value from binary literal you needed to parse it like that:

int i = Integer.parseInt("10110011", 2);

There is a set of problems with this implementation (it’s verbose, not popular two-argument parseInt(), RuntimeException on typo).

In Java 7 there is a solution – new syntax that fixes all issues and is neat:

int i = 0b10110011;

cute :)

Underscores in integers

In every day life, you prefer to have phone number written like this:

988343222

or like this:

988-343-222

This is why since Java 7 you can separate long integers with underscore to improve readability:

int milis = 3_600_000;

The underscore is removed by javac while generating .class files





Java 7 improved try-catch exception handling

7 11 2015

Java 8 with its Coin project introduced convenient exception handling with multi-catch blocks and final rethrows that make exception catching less verbose and more clean.

Multi-catch block

How often you catch two types of Exceptions and need to handle it in two separate catch blocks, even if the handle method does exact the same thing? Like that:

try{
  methodThatThrowsIOException();
  methodThatThrowsFileNotFoundException();
}catch(IOException e){
  System.out.println("Exception occured: " + e.getMessage());
}catch(FileNotFoundException e){
  System.out.println("Exception occured: " + e.getMessage());
}

Since Java 7 you can catch both types in one block with pipe operator:

try{
  methodThatThrowsIOException();
  methodThatThrowsFileNotFoundException();
}catch(IOException | FileNotFoundException e){
  System.out.println("Exception occured: " + e.getMessage());
}

Notice that here the e is of supertype Exception – it is not known at compile time. So you have access only to the Exception class API (not the IOException or FileNotFoundException). If you need type distinction, use the old try-catch API.

Final rethrow

Often times you want to rethrow caught exception to propagate it. Then you use throw mechanism and declare your method signature as throwing certain exception type. Before Java 7 yo could write:

public void myMethod() throws Exception{
  try{
    methodThatThrowsIOException();
    methodThatThrowsFileNotFoundException();
  }catch(Exception e){
    throw e;
  }
}

Notice that in that case you do not know the exception type at compile time, so myMethod() needs to throw the Exception type which is bad practice. The real type is missed in that block. On the other side you can easily notice that the exception types thrown in the try() block are IOException and FileNotFoundException. How to help compiler noticing that? Use keyword final:

public void myMethod() throws IOException, FileNotFoundException{
  try{
    methodThatThrowsIOException();
    methodThatThrowsFileNotFoundException();
  }catch(final Exception e){
    throw e;
  }
}

Now your method is aware of exception types and you can declare what subtypes are thrown.








%d bloggers like this: