decode URL in ANT - java

I am developing an ANT build script that should be invoked via another application (the Oxygen XML editor). This application passes some properties, one of which is a path, encoded as an URL (note: I don't have control over this input; it just is an URL). For example, the property ${project.url} is assigned file:/F:/projectpath/. In my ANT script, I'm stripping the file:/ part to get a path I can use in ANT tasks like <mkdir dir="${project.path}/_temp/>.
Yet, this appears to be too simplistic for more complex URLs. Suppose the project path contains a space. Then the ${project.url} property will be assigned file:/F:/project%20path/. Just stripping out the 'file:/ part results in a path that is still problematic to ANT tasks. In this case, the URL encoded space character (%20) should be decoded again to a space for the <mkdir/> task above to work.
Of course, this problem isn't limited to spaces, but applies to all characters that will end up escaped in the encoded URL. Therefore, the URL should be properly decoded in order to be of use in ANT tasks.
Is there an easy way in ANT (preferably without ANT-contrib) to:
decode a URL
assign this decoded URL to another property
...so that, given the property ${project.url} with value file:/F:/project%20path/, I can derive another property ${project.path} with value F:/project path/?

You can always run a <script> task to set a variable, then use that variable later in your build.xml.
For example:
https://ant.apache.org/manual/Tasks/script.html
<script language="groovy">
xmlfiles = new java.io.File(".").listFiles().findAll{ it =~ "\.xml$"}
xmlfiles.sort().each { self.log(it.toString())}
</script>

FoggyDay's excellent suggestion has set me on the right path to solve my problem. I've defined a <scriptdef> task that can be used to:
decode URLs to paths, and
store them in properties that can be used in the ANT file.
I can't program Java, so I've resorted to JavaScript and used its decodeURIComponent() function. Since this task works as a counterpart of the <makeurl> ANT task, I've named it 'makepath'.
Here's a working sample build file:
<project>
<property name="project.url" value="file:/F:/project%20path"/>
<scriptdef name="makepath" language="javascript">
<attribute name="url"/>
<attribute name="property"/>
<![CDATA[
var decodedUrl = decodeURIComponent(attributes.get("url"));
var path = decodedUrl.replace(/^[^:]+:\/+/g, "");
self.project.setProperty( attributes.get("property"), path )
]]>
</scriptdef>
<makepath url="${project.url}" property="project.path"/>
<echo>$project.path: ${project.path}</echo>
</project>
Of course this is just an illustrative example (otherwise there wouldn't be much point in explicitly declaring a URL if you need a path). But when you don't have control over the value of the ${project.url} property (which in my case is passed via an external program), this seems a handy way to convert it to something useful for the ANT context.

Related

Java: Possible to name a path with exactly 1 unknown directory?

Lets say I have a directory-structure.
/a/b/c/<unknown name>/d/e/f/<files>
for Windows:
C:\a\b\c\<unknown name>\d\e\f<files>
I know a/b/c is always there and also d/e/f/.
I do not know the directory () between them but I know there is only 1.
Is there a way in Java I can name this path without finding out the name of the 1 unknown directory to access ??
Like so?
/a/b/c/*/d/e/f
Yes, it is possible but probably not as straightforward as you think, you'd use the Files.walk method like follows:
Path root = Paths.get("S:\\Coding\\");
String prefix = "A\\AB";
String suffix = "B\\C";
Path searchRoot = root.resolve(prefix);
System.err.println(searchRoot);
List<Path> paths = Files.walk(searchRoot).filter(f -> f.endsWith(suffix)).collect(Collectors.toList());
paths.forEach(System.out::println);
Outputs:
stderr: S:\Coding\A\AB
stdout: S:\Coding\A\AB\ZZZ\B\C
Lets say you have a dockerized app based on a linux distribution.
You can run this unix command: find . -name d/e/f/yourFilename using Process Builder
This will return the complete filepath to your file which will include the unknown portion. And then you can assign it to a String and use in your Java app.
You can hardcode your search method as indicated in other answers. Or, to stay flexible match against patterns. You would need some pattern language to specify your path:
Shells typically use globbing.
Alternatively you could use regexp to distinguish wanted from unwanted files.
Once you have such a pattern matcher, use a tree walking algorithm (traverse the directory structure recursively), match each absolute path name with your pattern. If it matches, perform some action.
Be aware some globbing seems to exist in Java - see Match path string using glob in Java

Transforming JSON with XSLT using SaxonEE and Python

I am attempting to write a Python script that transforms JSON to a text file (CSV) with XSLT.
With saxon-ee-10.5.jar, I can successfully perform the desired transformation by running the following command (Windows 10):
java -cp saxon-ee-10.5.jar com.saxonica.Transform -it -xsl:styling.xslt -o:result.csv
How can I achieve the same result by using Python? I have been trying with Saxon-EE/C, but I am not sure if what I want to happen is possible.
Here is an example of what I have tried so far. My XSLT already defines an $in parameter for the initial.json file, but the PyXslt30Processor.apply_templates_returning_file() seems to require a call to PyXslt30Processor.set_initial_match_selection(), of which I am not sure if non-XML files can be passed.
from saxonc import PySaxonProcessor
with PySaxonProcessor(license=True) as proc:
xslt30proc = proc.new_xslt30_processor()
xslt30proc.set_initial_match_selection(file_name='initial.json')
content = xslt30proc.apply_templates_returning_file(
stylesheet_file='styling.xslt',
output_file='result.csv'
)
print(content)
Is what I want to accomplish possible with Saxon-EE/C, or should I try techniques of calling Java from Python?
I think you want to use call_template... instead of apply-templates, e.g. https://www.saxonica.com/saxon-c/doc/html/saxonc.html#PyXslt30Processor-call_template_returning_file with
xslt30proc.call_template_returning_file(None, stylesheet_file='styling.xslt',
output_file='result.csv'
)
Using None as the template name should be identical to using -it on the command line, i.e. start by calling the template named xsl:initial-template.
Don't use xslt30proc.set_initial_match_selection in that case.
It might, however, help, to set xslt30proc.set_cwd('.') before the call_template_returning_file call.

Nested jar: URI

The format of the jar: URI scheme is simply defined to be jar:<url>!/[<entry>].
Is it possible to "nest" two such URIs together and refer to a file inside an archive inside an archive this way? Something like:
jar:jar:http://example.com/!/archive!/file
This should designate /file inside an archive jar:http://example.com/!/archive, that is /archive found in a file provided by http://example.com/. However, URL.openConnection throws an exception:
java.net.MalformedURLException: no !/ in spec
I also cannot replace ! with %21 since that makes the jar: URI invalid, and I am not aware of any escaping that could be performed. Is this somehow possible? Or, as a more generic question, is it possible to store a URI with !/ inside <url> so that it is preserved?
As it is stated in the source code java.net.JarURLConnection does not handle nested Jar URIs.
Your example jar:jar:http://example.com/!/archive!/fileis parsed as entry archive!/file in the archive jar:http://example.com/. The latter is not a valid jar URI. You can not either escape the !/ sequences, because the text after jar: and before !/ must be a valid URI without any translation.
You can of course provide your own URLConnection implementation that supports nested Jar URIs.
While the other answer is indeed correct in the case of JarURLConnection, the use of jar: and similar URIs is not restricted to it. Such is the case of the Apache Commons VFS:
jar:// arch-file-uri[! absolute-path]
Where arch-file-uri refers to a file of any supported type, including other zip files. Note: if you would like to use the ! as normal character it must be escaped using %21.
"Normal character" means usage of the character in path to the file inside the archive, not in the inner URI. This makes jar:jar:http://example.com/!/archive!/file valid, as it is the last occurence of ! that should be used as a delimiter.
Note that the inner URI should probably still remain unescaped, thus it is impossible to use # there.

Enrich body with data from a file

My exchange body contains a class with a string attribute with path to file with which the body need to be enriched. There another attribute to hold file after enriched. I failed to find a solution to build a dynamic route that will load a file and return result I could use in Aggregation Strategy.
When you use the file component, you have to specify a path to a directory which can't be dynamic. You can only specify patterns on files in the directory.
Extract from the camel-file doc:
Camel supports only endpoints configured with a starting directory. So
the directoryName must be a directory. If you want to consume a single
file only, you can use the fileName option, e.g. by setting
fileName=thefilename. Also, the starting directory must not contain
dynamic expressions with ${ } placeholders. Again use the fileName
option to specify the dynamic part of the filename
Could you give us more info on what your are trying to achieve, so that we can give you workarounds ?

Getting the inputstream from a classpath resource (XML file)

In Java web application, Suppose if I want to get the InputStream of an XML file, which is placed in the CLASSPATH (i.e. inside the sources folder), how do I do it?
ClassLoader.getResourceAsStream().
As stated in the comment below, if you are in a multi-ClassLoader environment (such as unit testing, webapps, etc.) you may need to use Thread.currentThread().getContextClassLoader(). See http://stackoverflow.com/questions/2308188/getresourceasstream-vs-fileinputstream/2308388#comment21307593_2308388.
ClassLoader.class.getResourceAsStream("/path/file.ext");
That depends on where exactly the XML file is. Is it in the sources folder (in the "default package" or the "root") or in the same folder as the class?
In for former case, you must use "/file.xml" (note the leading slash) to find the file and it doesn't matter which class you use to try to locate it.
If the XML file is next to some class, SomeClass.class.getResourceAsStream() with just the filename is the way to go.
ClassLoader.class.getResourceAsStream("/path/to/your/xml") and make sure that your compile script is copying the xml file to where in your CLASSPATH.
someClassWithinYourSourceDir.getClass().getResourceAsStream();
Some of the "getResourceAsStream()" options in this answer didn't work for me, but this one did:
SomeClassWithinYourSourceDir.class.getClassLoader().getResourceAsStream("yourResource");
I tried proposed solution and forward slash in the file name did not work for me, example: ...().getResourceAsStream("/my.properties"); null was returned
Removing the slash worked: ....getResourceAsStream("my.properties");
Here is from doc API:
Before delegation, an absolute resource name is constructed from the given resource name using this algorithm:
If the name begins with a '/' ('\u002f'), then the absolute name of the resource is the portion of the name following the '/'.
Otherwise, the absolute name is of the following form:
modified_package_name/name
Where the modified_package_name is the package name of this object with '/' substituted for '.' ('\u002e').

Categories