Accepting both file paths and URL in Java - java

Is there a method which would accept the following paths and return appropriate URI:
test.xml // File in CWD
./test.xml // The same
../test.xml // File in parent directory
/etc/test.xml // Absolute path
file:///etc/test.xml // Full URL
http://example.com/test.xml // URL for http
Currently all I can think of is parse as url (URL.create) and if it fails attempt to try to parse it as File/Path.

If you want to use those URIs, for example in File constructor, you need to specify base URI for relative paths. You can do it with URI.resolve.
URI basePath = new URI("file:///base_dir/");
URI uri = basePath.resolve("foo.txt");
System.out.println(new File(uri));

Use URI, not URL.
This may or may not be what you actually want, however, depending on what you need to do with the result.

You can create an URI for each of the resources you pointed as follows:
public class T {
public static void main(final String[] args) throws URISyntaxException {
System.out.println(new URI("test.xml"));
System.out.println(new URI("./test.xml"));
System.out.println(new URI("../test.xml"));
System.out.println(new URI("/etc/test.xml"));
System.out.println(new URI("file:///etc/test.xml"));
System.out.println(new URI("http://example.com/test.xml"));
}
}
Additionally, you can retrieve the URL with the method "toURL()", but this just in case of the URI is absolute.

Related

is there a way to defined two path in JBehave story

I'm have a story where I need to supply the JSON config file path and a tool path and pass these two paths to the python script?
when I try to add a second path next to tool path it uses all the string after the path as path :/
is there a way how to supply two paths in 'Given'?
story looks like
Scenario: Verify Certificate and Cipher misconfiguration
Given TestSSL tool path 'C:\Users\Desktop\SSLTLS\testssl.sh-3.0'
Then Certificate and Cipher check should return 0 for successful security-check
java code
#Given("TestSSL tool path '$path'")
public void get_TestSSL_path(#Named("path") String path)
{
try
{
Paths.get(path);
toolPath = path;
}
catch (InvalidPathException | NullPointerException ex)
{
}
}
#Then("Certificate and Cipher check should return 0 for successful security-check")
public void verify_cipher_and_certificate_attributes() throws IOException, InterruptedException
{
String pythonFileName = "./scripts/python/security_misconfiguration_SSL_TLS.py";
String fullcmdCommand = jsonpath + " " + toolPath ;
System.out.println("Full path : " +fullcmdCommand);
int script_result = Utilities.runPythonScript(pythonFileName, fullcmdCommand);
Assert.assertEquals(0, script_result);
}
You should change the step input parameter to Collection, for example:
#Given("TestSSL tool paths '$paths'")
public void get_TestSSL_path(#Named("paths") List<String> paths)
...
Separate input data using comma, for example:
Given TestSSL tool paths 'C:\Users\Desktop\SSLTLS\testssl.sh-3.0, C:\Users\Desktop\SSLTLS\testssl.sh-4.0'

Assign String-Data to a URL-Object

Is it possible to assign string-data to a java.net.URL?
I'm using an API who's method only accept an URL-Parameter. But i already have the content.
String content = "some text data";
URL url = createURLWithContent(content);
MyApi.handleContent( url );
the createURLWithContent()-Method should create URL-object that containing the data from content, so when URL.getContent() is called, the String (or a Stream containing the String) is returned.
I think I've seen something like this years ago.
Write the content to a file then provide a file: URL, e.g.
public static URL createURLWithContent(String content) throws IOException {
Path tempFile = Files.createTempFile("UrlContent", null);
tempFile.toFile().deleteOnExit();
Files.writeString(tempFile, content);
return tempFile.toUri().toURL();
}
This will return a URL like this:
file:/C:/path/to/temp/UrlContent5767435257817348076.tmp
If your program is long-running, you should handle the deletion of the temporary file yourself, after the API call, rather than rely on deleteOnExit().
If you don't want the content on disk, you could alternatively implement your own in-memory URLStreamHandler, and specify it when creating the URL using the URL(String protocol, String host, int port, String file, URLStreamHandler handler) constructor. This is a lot more involved and beyond the scope of this answer.

How to construct a file from a relative path in a File

I'm parsing a base file at this location:
/Users/haddad/development/fspc/content/2017.dev/src/agency/individual/integration/src/forms/print.xml
And from that file, I parse out:
../../../../include/masking.xml
So that relative path is from the context from the file I parsed it out from (base file)
How can I constuct a file object from that relative path so I can read it's contents?
Since you tagged nio, the Path class makes this easy. You simply call resolveSibling() and normalize().
String main = "/Users/haddad/development/fspc/content/2017.dev/src/agency/individual/integration/src/forms/print.xml";
String ref = "../../../../include/masking.xml";
System.out.println(Paths.get(main));
System.out.println(Paths.get(main).resolveSibling(ref));
System.out.println(Paths.get(main).resolveSibling(ref).normalize());
Or:
System.out.println(Paths.get(main));
System.out.println(Paths.get(main, ref));
System.out.println(Paths.get(main, ref).normalize());
Output
\Users\haddad\development\fspc\content\2017.dev\src\agency\individual\integration\src\forms\print.xml
\Users\haddad\development\fspc\content\2017.dev\src\agency\individual\integration\src\forms\..\..\..\..\include\masking.xml
\Users\haddad\development\fspc\content\2017.dev\src\agency\include\masking.xml
Note: I ran this on a Window machine, so I of course got backslashes
If you prefer the old File object, you use the two-argument constructor, and call getCanonicalFile().
System.out.println(new File(main));
System.out.println(new File(main, ref));
System.out.println(new File(main, ref).getCanonicalFile());
Output
\Users\haddad\development\fspc\content\2017.dev\src\agency\individual\integration\src\forms\print.xml
\Users\haddad\development\fspc\content\2017.dev\src\agency\individual\integration\src\forms\print.xml\..\..\..\..\include\masking.xml
C:\Users\haddad\development\fspc\content\2017.dev\src\agency\individual\include\masking.xml
You could use subpath() to keep the path part that interests you that you can combine with resolve() to append a new path to :
public static void main(String[] args) {
Path printXmlPath = Paths.get("/Users/haddad/development/fspc/content/2017.dev/src/agency/individual/integration/src/forms/print.xml");
Path maskingXmlPath = printXmlPath.subpath(0, printXmlPath.getNameCount() - 5)
.resolve("include/masking.xml");
System.out.println(maskingXmlPath);
}
Users\haddad\development\fspc\content\2017.dev\src\agency\include\masking.xml

Java 7 zip file system provider doesn't seem to accept spaces in URI

I have been testing all possible variations and permutations, but I can't seem to construct a FileSystemProvider with the zip/jar scheme for a path (URI) that contains spaces. There is a very simplistic test case available at Oracle Docs. I took the liberty of modifying the example and just adding spaces to the URI, and it stops working. Snippet below:
import java.util.*;
import java.net.URI;
import java.nio.file.*;
public class Test {
public static void main(String [] args) throws Throwable {
Map<String, String> env = new HashMap<>();
env.put("create", "true");
URI uri = new URI("jar:file:/c:/dir%20with%20spaces/zipfstest.zip");
Path dir = Paths.get("C:\\dir with spaces");
if(Files.exists(dir) && Files.isDirectory(dir)) {
try (FileSystem zipfs = FileSystems.newFileSystem(uri, env)) {}
}
}
}
When I execute this code (Windows, JDK7u2, both x32 and x64), I get the following exception:
java.lang.IllegalArgumentException: Illegal character in path at index 12: file:/c:/dir with spaces/zipfstest.zip
at com.sun.nio.zipfs.ZipFileSystemProvider.uriToPath(ZipFileSystemProvider.java:87)
at com.sun.nio.zipfs.ZipFileSystemProvider.newFileSystem(ZipFileSystemProvider.java:107)
at java.nio.file.FileSystems.newFileSystem(FileSystems.java:322)
at java.nio.file.FileSystems.newFileSystem(FileSystems.java:272)
If I use + instead of %20 as the space escape character, a different exception is thrown:
java.nio.file.NoSuchFileException: c:\dir+with+spaces\zipfstest.zip
at sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:79)
at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:97)
at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:102)
at sun.nio.fs.WindowsFileSystemProvider.newByteChannel(WindowsFileSystemProvider.java:229)
at java.nio.file.spi.FileSystemProvider.newOutputStream(FileSystemProvider.java:430)
at java.nio.file.Files.newOutputStream(Files.java:170)
at com.sun.nio.zipfs.ZipFileSystem.<init>(ZipFileSystem.java:116)
at com.sun.nio.zipfs.ZipFileSystemProvider.newFileSystem(ZipFileSystemProvider.java:117)
at java.nio.file.FileSystems.newFileSystem(FileSystems.java:322)
at java.nio.file.FileSystems.newFileSystem(FileSystems.java:272)
I might be missing something very obvious, but would this indicate a problem with the supplied ZIP/JAR file system provider?
EDIT:
Another use case based on a File object, as requested in coments:
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.nio.file.FileSystems;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Test {
public static void main(String[] args) throws UnsupportedEncodingException {
try {
File zip = new File("C:\\dir with spaces\\file.zip");
URI uri = URI.create("jar:" + zip.toURI().toURL());
Map<String, String> env = new HashMap<>();
env.put("create", "true");
if(zip.getParentFile().exists() && zip.getParentFile().isDirectory()) {
FileSystems.newFileSystem(uri, env);
}
} catch (Exception ex) {
Logger.getAnonymousLogger().log(Level.SEVERE, null, ex);
System.out.println();
}
}
}
The exception is thrown again as:
java.lang.IllegalArgumentException: Illegal character in path at index 12: file:/C:/dir with spaces/file.zip
at com.sun.nio.zipfs.ZipFileSystemProvider.uriToPath(ZipFileSystemProvider.java:87)
at com.sun.nio.zipfs.ZipFileSystemProvider.newFileSystem(ZipFileSystemProvider.java:107)
at java.nio.file.FileSystems.newFileSystem(FileSystems.java:322)
at java.nio.file.FileSystems.newFileSystem(FileSystems.java:272)
Actually further analysis does seem to indicate there is a problem with the ZipFileSystemProvider. The uriToPath(URI uri) method contained within the class executes the following snippet:
String spec = uri.getSchemeSpecificPart();
int sep = spec.indexOf("!/");
if (sep != -1)
spec = spec.substring(0, sep);
return Paths.get(new URI(spec)).toAbsolutePath();
From the JavaDocs of URI.getSchemeSpecificPart() we can see the following:
The string returned by this method is equal to that returned by the
getRawSchemeSpecificPart method except that all sequences of escaped
octets are decoded.
This same string is then passed back as an argument into the new URI() constructor. Since any escaped octets are de-escaped by getSchemeSpecificPart(), if the original URI contained any escape characters, they will not be propagated to the new URI - hence the exception.
A potential workaround - loop through all the available filesystem providers and get the reference to the one who's spec equals "jar". Then use that to create a new filesystem based on path only.
This is a bug in Java 7 and it has been marked as fixed in Java 8 (see Bug ID 7156873). The fix should also be backported to Java 7, but at the moment it's not determined that which update will have it (see Bug ID 8001178).
The jar: URIs should have the escaped zip-URI in its scheme-specific part, so your jar: URI is simply wrong - it should rightly be double-escaped, as the jar: scheme is composed of the host URI, !/ and the local path.
However, this escaping is only implied and not expressed by the minimal URL "specification" in JarURLConnection. I agree however with the raised bug in JRE that it should still accept single-escaped, although that could lead to some strange edge-cases not being supported.
As pointed out by tornike and evermean in another answer, the easiest is to do FileSystems.newFileSystem(path, null) - but this does not work when you want to pass and env with say "create"=true.
Instead, create the jar: URI using the component-based constructor:
URI jar = new URI("jar", path.toUri().toString(), null);
This would properly encode the scheme-specific part.
As a JUnit test, which also confirms that this is the escaping used when opening from a Path:
#Test
public void jarWithSpaces() throws Exception {
Path path = Files.createTempFile("with several spaces", ".zip");
Files.delete(path);
// Will fail with FileSystemNotFoundException without env:
//FileSystems.newFileSystem(path, null);
// Neither does this work, as it does not double-escape:
// URI jar = URI.create("jar:" + path.toUri().toASCIIString());
URI jar = new URI("jar", path.toUri().toString(), null);
assertTrue(jar.toASCIIString().contains("with%2520several%2520spaces"));
Map<String, Object> env = new HashMap<>();
env.put("create", "true");
try (FileSystem fs = FileSystems.newFileSystem(jar, env)) {
URI root = fs.getPath("/").toUri();
assertTrue(root.toString().contains("with%2520several%2520spaces"));
}
// Reopen from now-existing Path to check that the URI is
// escaped in the same way
try (FileSystem fs = FileSystems.newFileSystem(path, null)) {
URI root = fs.getPath("/").toUri();
//System.out.println(root.toASCIIString());
assertTrue(root.toString().contains("with%2520several%2520spaces"));
}
}
(I did a similar test with "with\u2301unicode\u263bhere" to check that I did not need to use .toASCIIString())
There are two methods to create a filesystem:
FileSystem fs = FileSystems.newFileSystem(uri, env);
FileSystem fs = FileSystems.newFileSystem(zipfile, null);
When there is a space in a filename together with the above solution for creating a uri. It also works if you use a different method that doesn't take a uri as argument.

URL formatting in Java

String arg="http://www.example.com/user.php?id=<URLRequest Method='GetByUID' />";
java.net.URI uri = new java.net.URI( arg );
java.awt.Desktop desktop = java.awt.Desktop.getDesktop();
desktop.browse( uri );
I want to open the given link in default browser with the above code but it says the url is invalid...i tried escaping characters like ' also but its not working.
If i replace String arg="www.google.com"; then there is no problem and I am able to open google.com.
Please help.
Your string contains characters that aren't valid in a URI, per RFC 2396. You need to properly encode the query parameters. Many utilities support that, like the standard URLEncoder (lower level), JAX-RS UriBuilder, Spring UriUtils, Apache HttpClient URLEncodedUtils and so on.
Edit: Oh, and the URI class can handle it, too, but you have to use a different constructor:
URI uri = new URI("http", "foo.com", null, "a=<some garbage>&b= |{$0m3 m0r3 garbage}| &c=imokay", null);
System.out.println(uri);
Outputs:
http://foo.com?a=%3Csome%20garbage%3E&b=%20%7C%7B$0m3%20m0r3%20garbage%7D%7C%20&c=imokay
which, while ugly, is the correct representation.
Thats because it is invalid. <URLRequest Method='GetByUID' /> should be replaced by the value of the id, or an expression that returns the id which you can concatenate with the arg string. Something like
String arg="http://www.example.com/user.php?id="+getByUID(someUid);
import java.net.URL;
import java.net.URLEncoder;
class ARealURL {
public static void main(String[] args) throws Exception {
String s1 = "http://www.example.com/user.php?id=";
String param = "<URLRequest Method='GetByUID' />";
String encodedParam = URLEncoder.encode(param,"UTF-8");
URL url = new URL(s1+encodedParam);
System.out.println(url);
}
}
Output
http://www.example.com/user.php?id=%3CURLRequest+Method%3D%27GetByUID%27+%2F%3E

Categories