java Gherkin parser stream does not release file locks - java

I am using Gherkin parser to parse feature files and returning the list of Gherkin documents see the function below:
import io.cucumber.gherkin.Gherkin;
import io.cucumber.messages.IdGenerator;
import io.cucumber.messages.Messages;
import io.cucumber.messages.Messages.Envelope;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class GherkinUtils {
private static final Logger LOG = LogManager.getLogger(GherkinUtils.class);
public static ArrayList<Messages.GherkinDocument> getGherkinDocumentsFromFiles() {
IdGenerator idGenerator = new IdGenerator.Incrementing();
ArrayList<Messages.GherkinDocument> listOfGherkinDocuments = new ArrayList<>();
String pathFolderFrameworkFeatures = SettingsUtils.getPathFolderFrameworkFeatures();
List<String> listOfPathsForFeatureFiles = FileUtils.getAllFilePathsFromFolder(pathFolderFrameworkFeatures);
try (Stream<Envelope> dataStream = Gherkin.fromPaths(listOfPathsForFeatureFiles, false, true, false, idGenerator)){
List<Envelope> envelopes = dataStream.collect(Collectors.toList());
for (Envelope env : envelopes) {
Messages.GherkinDocument gherkinDocument = env.getGherkinDocument();
listOfGherkinDocuments.add(gherkinDocument);
}
} catch (Exception e) {
LOG.error("Error occurred while trying to read the feature files", new Exception(e));
}
FileUtils.renameAllFeatureFiles("b");
return listOfGherkinDocuments;
}
}
Just before the return statement, you can see the function that will update the name for all feature files just to check if they are not locked.
The problem is that only the first file is always renamed and the rest of them are always locked.
If I will place the rename function at the top, then all the files are successfully renamed...
My understanding is that the try statement will automatically close the stream. Also, I tried to close it manually inside the try block but the results are the same.
What am I missing? How can I make it to release the file locks?
Update 1:
This exact line is making the files (except the first one to be locked):
List<Envelope> envelopes = dataStream.collect(Collectors.toList());
Here is the file name update function definition in case you want to test it:
public static void renameAllFeatureFiles(String fileName) {
String pathFeaturesFolder = SettingsUtils.getPathFolderFrameworkFeatures();
List<String> pathList = FileUtils.getAllFilePathsFromFolder(pathFeaturesFolder);
int counter = 0;
for (String path : pathList) {
counter ++;
File file = new File(path);
File newFile = new File(pathFeaturesFolder + "\\" + fileName +counter+".feature");
System.out.println("File: " + path + " locked: " + !file.renameTo(newFile));
}
}
And here is a sample feature file content:
Feature: Test
Scenario: test 1
Given User will do something
And User will do something
Update 2:
Tried with separate thread using javafx Task, still the same issue :(
Except for one file (this is really strange) all files are locked...
public static void runInNewThread() {
// define the execution task that will run in a new thread
Task<Void> newTask = new Task<>() {
#Override
protected Void call() {
ArrayList<Messages.GherkinDocument> listOfGherkinDocuments = GherkinUtils.getGherkinDocumentsFromFiles();
return null;
}
};
// run the task in a new thread
Thread th = new Thread(newTask);
th.setDaemon(true);
th.start();
}

For now, I have used workaround with creating copies of the specific files and using parser on the copies to prevent locking of the original versions...

Related

How can I copy attribute in java weka?

I am using Weka libraries for feature selection problem solution. I have read data as follows : The data set is in arff format
BufferedReader reader = new BufferedReader(new FileReader("D:\\Exp\\golf.arff"));
Instances data = new Instances(reader);
reader.close();
data.setClassIndex(data.numAttributes() - 1);
For example, attributes are Temperature, humidity, Windy, Outlook and the last one is class.
Now I want to store Temperature attribute as instances. The new data will consists only temperature (and it should be instance type because in further processing I have to use methods of instance)
Quick google search brought this:
Instead of picking the features just remove all features that you don't need.
import java.io.File;
import weka.core.Instances;
import weka.core.converters.ArffLoader;
import weka.core.converters.ArffSaver;
import weka.filters.Filter;
import weka.filters.unsupervised.attribute.Remove;
public class Convert4 {
public static void main(String[] args) {
// TODO Auto-generated method stub
try
{
ArffLoader loader2= new ArffLoader();
loader2.setSource(new File("C:/Users/RAHUL/Desktop/stack.arff"));
Instances data2= loader2.getDataSet();
//Load Arff
String[] options = new String[2];
options[0] = "-R"; // "range"
options[1] = "1"; // first attribute
Remove remove = new Remove(); // new instance of filter
remove.setOptions(options); // set options
remove.setInputFormat(data2); // inform filter about dataset **AFTER** setting options
Instances newData2 = Filter.useFilter(data2, remove); // apply filter
ArffSaver saver = new ArffSaver();
saver.setInstances(newData2);
saver.setFile(new File("C:/Users/RAHUL/Desktop/stack2.arff"));
saver.writeBatch();
}
catch (Exception e)
{}
}
}
Answer comes from:
How to remove particular attributes from arff file and produce modified arff?

Detect file type based on content

Tried the following:
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.spi.FileTypeDetector;
import org.apache.tika.Tika;
import org.apache.tika.mime.MimeTypes;
/**
*
* #author kiriti.k
*/
public class TikaFileTypeDetector {
private final Tika tika = new Tika();
public TikaFileTypeDetector() {
super();
}
public String probeContentType(Path path) throws IOException {
// Try to detect based on the file name only for efficiency
String fileNameDetect = tika.detect(path.toString());
if (!fileNameDetect.equals(MimeTypes.OCTET_STREAM)) {
return fileNameDetect;
}
// Then check the file content if necessary
String fileContentDetect = tika.detect(path.toFile());
if (!fileContentDetect.equals(MimeTypes.OCTET_STREAM)) {
return fileContentDetect;
}
// Specification says to return null if we could not
// conclusively determine the file type
return null;
}
public static void main(String[] args) throws IOException {
Tika tika = new Tika();
// expects file path as the program argument
if (args.length != 1) {
printUsage();
return;
}
Path path = Paths.get(args[0]);
TikaFileTypeDetector detector = new TikaFileTypeDetector();
// Analyse the file - first based on file name for efficiency.
// If cannot determine based on name and then analyse content
String contentType = detector.probeContentType(path);
System.out.println("File is of type - " + contentType);
}
public static void printUsage() {
System.out.print("Usage: java -classpath ... "
+ TikaFileTypeDetector.class.getName()
+ " ");
}
}
The above program is checking based on file extension only. How do I make it to check content type also(mime) and then determine the type. I am using tika-app-1.8.jar in netbean 8.0.2. What am I missing?
The code checks the file extension first and returns the MIME type based on that, if it finds a result. If you want it to check the content first, just switch the two statements:
public String probeContentType(Path path) throws IOException {
// Check contents first
String fileContentDetect = tika.detect(path.toFile());
if (!fileContentDetect.equals(MimeTypes.OCTET_STREAM)) {
return fileContentDetect;
}
// Try file name only if content search was not successful
String fileNameDetect = tika.detect(path.toString());
if (!fileNameDetect.equals(MimeTypes.OCTET_STREAM)) {
return fileNameDetect;
}
// Specification says to return null if we could not
// conclusively determine the file type
return null;
}
Be aware that this may have huge performance impact.
You can use Files.probeContentType(path)

How can I make Spark Streaming count the words in a file in a unit test?

I've successfully built a very simple Spark Streaming application in Java that is based on the HdfsCount example in Scala.
When I submit this application to my local Spark, it waits for a file to be written to a given directory, and when I create that file it successfully prints the number of words. I terminate the application by pressing Ctrl+C.
Now I've tried to create a very basic unit test for this functionality, but in the test I was not able to print the same information, that is the number of words.
What am I missing?
Below is the unit test file, and after that I've also included the code snippet that shows the countWords method:
StarterAppTest.java
import com.google.common.io.Files;
import org.apache.spark.streaming.Duration;
import org.apache.spark.streaming.api.java.JavaDStream;
import org.apache.spark.streaming.api.java.JavaPairDStream;
import org.apache.spark.streaming.api.java.JavaStreamingContext;
import org.junit.*;
import java.io.*;
public class StarterAppTest {
JavaStreamingContext ssc;
File tempDir;
#Before
public void setUp() {
ssc = new JavaStreamingContext("local", "test", new Duration(3000));
tempDir = Files.createTempDir();
tempDir.deleteOnExit();
}
#After
public void tearDown() {
ssc.stop();
ssc = null;
}
#Test
public void testInitialization() {
Assert.assertNotNull(ssc.sc());
}
#Test
public void testCountWords() {
StarterApp starterApp = new StarterApp();
try {
JavaDStream<String> lines = ssc.textFileStream(tempDir.getAbsolutePath());
JavaPairDStream<String, Integer> wordCounts = starterApp.countWords(lines);
ssc.start();
File tmpFile = new File(tempDir.getAbsolutePath(), "tmp.txt");
PrintWriter writer = new PrintWriter(tmpFile, "UTF-8");
writer.println("8-Dec-2014: Emre Emre Emre Ergin Ergin Ergin");
writer.close();
System.err.println("===== Word Counts =======");
wordCounts.print();
System.err.println("===== Word Counts =======");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
Assert.assertTrue(true);
}
}
This test compiles and starts to run, Spark Streaming prints a lot of diagnostic messages on the console but the call to wordCounts.print() does not print anything, whereas in StarterApp.java itself, they do.
I've also tried adding ssc.awaitTermination(); after ssc.start() but nothing changed in that respect. After that I've also tried to create a new file manually in the directory that this Spark Streaming application was checking but this time it gave an error.
For completeness, below is the wordCounts method:
public JavaPairDStream<String, Integer> countWords(JavaDStream<String> lines) {
JavaDStream<String> words = lines.flatMap(new FlatMapFunction<String, String>() {
#Override
public Iterable<String> call(String x) { return Lists.newArrayList(SPACE.split(x)); }
});
JavaPairDStream<String, Integer> wordCounts = words.mapToPair(
new PairFunction<String, String, Integer>() {
#Override
public Tuple2<String, Integer> call(String s) { return new Tuple2<>(s, 1); }
}).reduceByKey((i1, i2) -> i1 + i2);
return wordCounts;
}
Few pointers:
Give at least 2 cores to SparkStreaming context. 1 for the Streaming and 1 for the Spark processing. "local" -> "local[2]"
Your streaming interval is of 3000ms, so somewhere in your program you need to wait -at least- that time to expect an output.
Spark Streaming needs some time for the setup of listeners. The file is being created immediately after ssc.start is issued. There's no warranty that the filesystem listener is already in place. I'd do some sleep(xx) after ssc.start
In Streaming, it's all about the right timing.

Integration of Rapidminer with Java: Obtaining the output Example Set (Process Result)

I want to execute a Rapidminer process from Java to use the output ExampleSet (Process Result) for subsequent operations (with Java).
I managed the process execution with the code below, but I don't have a clue how to obtain the process Result Example Set.
Ideally, I want to get any Example Set independent of the variables, but if you need to generate the metadata beforehand, will have to be.
package com.companyname.rm;
import com.rapidminer.Process;
import com.rapidminer.RapidMiner;
import com.rapidminer.operator.OperatorException;
import com.rapidminer.tools.XMLException;
import java.io.File;
import java.io.IOException;
public class RunProcess {
public static void main(String[] args) {
try {
RapidMiner.setExecutionMode(RapidMiner.ExecutionMode.COMMAND_LINE);
RapidMiner.init();
Process process = new Process(new File("//my_path/..../test_JAVA.rmp"));
process.run();
} catch (IOException | XMLException | OperatorException ex) {
ex.printStackTrace();
}
}
}
To obtain the ExampleSet of the process you need add
IOContainer ioResult = process.run();
A shortened example taken from http://allinoneat.blogspot.de/2013/04/integrate-rapidminer-wtih-java.html
IOContainer ioResult = process.run();
if (ioResult.getElementAt(0) instanceof ExampleSet) {
ExampleSet resultSet = (ExampleSet) ioResult.getElementAt(0);
for (Example example : resultSet) {
Iterator<Attribute> allAtts = example.getAttributes().allAttributes();
while (allAtts.hasNext()) {
Attribute a = allAtts.next();
if (a.isNumerical()) {
double value = example.getValue(a);
System.out.print(value + " ");
} else {
String value = example.getValueAsString(a);
System.out.print(value + " ");
}
}
System.out.println("\n");
}
}
Option 1:
Click on context, and save process output to files.
Then, read from files.
Option2:
Use the WriteAsText to save what you want. Then read from the file.
I just run the rapidminer as a script:
http://rapid-i.com/rapidforum/index.php?topic=3009.0

How to list the files inside a JAR file?

I have this code which reads all the files from a directory.
File textFolder = new File("text_directory");
File [] texFiles = textFolder.listFiles( new FileFilter() {
public boolean accept( File file ) {
return file.getName().endsWith(".txt");
}
});
It works great. It fills the array with all the files that end with ".txt" from directory "text_directory".
How can I read the contents of a directory in a similar fashion within a JAR file?
So what I really want to do is, to list all the images inside my JAR file, so I can load them with:
ImageIO.read(this.getClass().getResource("CompanyLogo.png"));
(That one works because the "CompanyLogo" is "hardcoded" but the number of images inside the JAR file could be from 10 to 200 variable length.)
EDIT
So I guess my main problem would be: How to know the name of the JAR file where my main class lives?
Granted I could read it using java.util.Zip.
My Structure is like this:
They are like:
my.jar!/Main.class
my.jar!/Aux.class
my.jar!/Other.class
my.jar!/images/image01.png
my.jar!/images/image02a.png
my.jar!/images/imwge034.png
my.jar!/images/imagAe01q.png
my.jar!/META-INF/manifest
Right now I'm able to load for instance "images/image01.png" using:
ImageIO.read(this.getClass().getResource("images/image01.png));
But only because I know the file name, for the rest I have to load them dynamically.
CodeSource src = MyClass.class.getProtectionDomain().getCodeSource();
if (src != null) {
URL jar = src.getLocation();
ZipInputStream zip = new ZipInputStream(jar.openStream());
while(true) {
ZipEntry e = zip.getNextEntry();
if (e == null)
break;
String name = e.getName();
if (name.startsWith("path/to/your/dir/")) {
/* Do something with this entry. */
...
}
}
}
else {
/* Fail... */
}
Note that in Java 7, you can create a FileSystem from the JAR (zip) file, and then use NIO's directory walking and filtering mechanisms to search through it. This would make it easier to write code that handles JARs and "exploded" directories.
Code that works for both IDE's and .jar files:
import java.io.*;
import java.net.*;
import java.nio.file.*;
import java.util.*;
import java.util.stream.*;
public class ResourceWalker {
public static void main(String[] args) throws URISyntaxException, IOException {
URI uri = ResourceWalker.class.getResource("/resources").toURI();
Path myPath;
if (uri.getScheme().equals("jar")) {
FileSystem fileSystem = FileSystems.newFileSystem(uri, Collections.<String, Object>emptyMap());
myPath = fileSystem.getPath("/resources");
} else {
myPath = Paths.get(uri);
}
Stream<Path> walk = Files.walk(myPath, 1);
for (Iterator<Path> it = walk.iterator(); it.hasNext();){
System.out.println(it.next());
}
}
}
erickson's answer worked perfectly:
Here's the working code.
CodeSource src = MyClass.class.getProtectionDomain().getCodeSource();
List<String> list = new ArrayList<String>();
if( src != null ) {
URL jar = src.getLocation();
ZipInputStream zip = new ZipInputStream( jar.openStream());
ZipEntry ze = null;
while( ( ze = zip.getNextEntry() ) != null ) {
String entryName = ze.getName();
if( entryName.startsWith("images") && entryName.endsWith(".png") ) {
list.add( entryName );
}
}
}
webimages = list.toArray( new String[ list.size() ] );
And I have just modify my load method from this:
File[] webimages = ...
BufferedImage image = ImageIO.read(this.getClass().getResource(webimages[nextIndex].getName() ));
To this:
String [] webimages = ...
BufferedImage image = ImageIO.read(this.getClass().getResource(webimages[nextIndex]));
I would like to expand on acheron55's answer, since it is a very non-safe solution, for several reasons:
It doesn't close the FileSystem object.
It doesn't check if the FileSystem object already exists.
It isn't thread-safe.
This is somewhat a safer solution:
private static ConcurrentMap<String, Object> locks = new ConcurrentHashMap<>();
public void walk(String path) throws Exception {
URI uri = getClass().getResource(path).toURI();
if ("jar".equals(uri.getScheme()) {
safeWalkJar(path, uri);
} else {
Files.walk(Paths.get(path));
}
}
private void safeWalkJar(String path, URI uri) throws Exception {
synchronized (getLock(uri)) {
// this'll close the FileSystem object at the end
try (FileSystem fs = getFileSystem(uri)) {
Files.walk(fs.getPath(path));
}
}
}
private Object getLock(URI uri) {
String fileName = parseFileName(uri);
locks.computeIfAbsent(fileName, s -> new Object());
return locks.get(fileName);
}
private String parseFileName(URI uri) {
String schemeSpecificPart = uri.getSchemeSpecificPart();
return schemeSpecificPart.substring(0, schemeSpecificPart.indexOf("!"));
}
private FileSystem getFileSystem(URI uri) throws IOException {
try {
return FileSystems.getFileSystem(uri);
} catch (FileSystemNotFoundException e) {
return FileSystems.newFileSystem(uri, Collections.<String, String>emptyMap());
}
}
There's no real need to synchronize over the file name; one could simply synchronize on the same object every time (or make the method synchronized), it's purely an optimization.
I would say that this is still a problematic solution, since there might be other parts in the code that use the FileSystem interface over the same files, and it could interfere with them (even in a single threaded application).
Also, it doesn't check for nulls (for instance, on getClass().getResource().
This particular Java NIO interface is kind of horrible, since it introduces a global/singleton non thread-safe resource, and its documentation is extremely vague (a lot of unknowns due to provider specific implementations). Results may vary for other FileSystem providers (not JAR). Maybe there's a good reason for it being that way; I don't know, I haven't researched the implementations.
So I guess my main problem would be, how to know the name of the jar where my main class lives.
Assuming that your project is packed in a Jar (not necessarily true!), you can use ClassLoader.getResource() or findResource() with the class name (followed by .class) to get the jar that contains a given class. You'll have to parse the jar name from the URL that gets returned (not that tough), which I will leave as an exercise for the reader :-)
Be sure to test for the case where the class is not part of a jar.
I've ported acheron55's answer to Java 7 and closed the FileSystem object. This code works in IDE's, in jar files and in a jar inside a war on Tomcat 7; but note that it does not work in a jar inside a war on JBoss 7 (it gives FileSystemNotFoundException: Provider "vfs" not installed, see also this post). Furthermore, like the original code, it is not thread safe, as suggested by errr. For these reasons I have abandoned this solution; however, if you can accept these issues, here is my ready-made code:
import java.io.IOException;
import java.net.*;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Collections;
public class ResourceWalker {
public static void main(String[] args) throws URISyntaxException, IOException {
URI uri = ResourceWalker.class.getResource("/resources").toURI();
System.out.println("Starting from: " + uri);
try (FileSystem fileSystem = (uri.getScheme().equals("jar") ? FileSystems.newFileSystem(uri, Collections.<String, Object>emptyMap()) : null)) {
Path myPath = Paths.get(uri);
Files.walkFileTree(myPath, new SimpleFileVisitor<Path>() {
#Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
System.out.println(file);
return FileVisitResult.CONTINUE;
}
});
}
}
}
Here is an example of using Reflections library to recursively scan classpath by regex name pattern augmented with a couple of Guava perks to to fetch resources contents:
Reflections reflections = new Reflections("com.example.package", new ResourcesScanner());
Set<String> paths = reflections.getResources(Pattern.compile(".*\\.template$"));
Map<String, String> templates = new LinkedHashMap<>();
for (String path : paths) {
log.info("Found " + path);
String templateName = Files.getNameWithoutExtension(path);
URL resource = getClass().getClassLoader().getResource(path);
String text = Resources.toString(resource, StandardCharsets.UTF_8);
templates.put(templateName, text);
}
This works with both jars and exploded classes.
Here's a method I wrote for a "run all JUnits under a package". You should be able to adapt it to your needs.
private static void findClassesInJar(List<String> classFiles, String path) throws IOException {
final String[] parts = path.split("\\Q.jar\\\\E");
if (parts.length == 2) {
String jarFilename = parts[0] + ".jar";
String relativePath = parts[1].replace(File.separatorChar, '/');
JarFile jarFile = new JarFile(jarFilename);
final Enumeration<JarEntry> entries = jarFile.entries();
while (entries.hasMoreElements()) {
final JarEntry entry = entries.nextElement();
final String entryName = entry.getName();
if (entryName.startsWith(relativePath)) {
classFiles.add(entryName.replace('/', File.separatorChar));
}
}
}
}
Edit:
Ah, in that case, you might want this snippet as well (same use case :) )
private static File findClassesDir(Class<?> clazz) {
try {
String path = clazz.getProtectionDomain().getCodeSource().getLocation().getFile();
final String codeSourcePath = URLDecoder.decode(path, "UTF-8");
final String thisClassPath = new File(codeSourcePath, clazz.getPackage().getName().repalce('.', File.separatorChar));
} catch (UnsupportedEncodingException e) {
throw new AssertionError("impossible", e);
}
}
Just to mention that if you are already using Spring, you can take advantage of the PathMatchingResourcePatternResolver.
For instance to get all the PNG files from a images folder in resources
ClassLoader cl = this.getClass().getClassLoader();
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(cl);
Resource[] resources = resolver.getResources("images/*.png");
for (Resource r: resources){
logger.info(r.getFilename());
// From your example
// ImageIO.read(cl.getResource("images/" + r.getFilename()));
}
A jar file is just a zip file with a structured manifest. You can open the jar file with the usual java zip tools and scan the file contents that way, inflate streams, etc. Then use that in a getResourceAsStream call, and it should be all hunky dory.
EDIT / after clarification
It took me a minute to remember all the bits and pieces and I'm sure there are cleaner ways to do it, but I wanted to see that I wasn't crazy. In my project image.jpg is a file in some part of the main jar file. I get the class loader of the main class (SomeClass is the entry point) and use it to discover the image.jpg resource. Then some stream magic to get it into this ImageInputStream thing and everything is fine.
InputStream inputStream = SomeClass.class.getClassLoader().getResourceAsStream("image.jpg");
JPEGImageReaderSpi imageReaderSpi = new JPEGImageReaderSpi();
ImageReader ir = imageReaderSpi.createReaderInstance();
ImageInputStream iis = new MemoryCacheImageInputStream(inputStream);
ir.setInput(iis);
....
ir.read(0); //will hand us a buffered image
Given an actual JAR file, you can list the contents using JarFile.entries(). You will need to know the location of the JAR file though - you can't just ask the classloader to list everything it could get at.
You should be able to work out the location of the JAR file based on the URL returned from ThisClassName.class.getResource("ThisClassName.class"), but it may be a tiny bit fiddly.
Some time ago I made a function that gets classess from inside JAR:
public static Class[] getClasses(String packageName)
throws ClassNotFoundException{
ArrayList<Class> classes = new ArrayList<Class> ();
packageName = packageName.replaceAll("\\." , "/");
File f = new File(jarName);
if(f.exists()){
try{
JarInputStream jarFile = new JarInputStream(
new FileInputStream (jarName));
JarEntry jarEntry;
while(true) {
jarEntry=jarFile.getNextJarEntry ();
if(jarEntry == null){
break;
}
if((jarEntry.getName ().startsWith (packageName)) &&
(jarEntry.getName ().endsWith (".class")) ) {
classes.add(Class.forName(jarEntry.getName().
replaceAll("/", "\\.").
substring(0, jarEntry.getName().length() - 6)));
}
}
}
catch( Exception e){
e.printStackTrace ();
}
Class[] classesA = new Class[classes.size()];
classes.toArray(classesA);
return classesA;
}else
return null;
}
public static ArrayList<String> listItems(String path) throws Exception{
InputStream in = ClassLoader.getSystemClassLoader().getResourceAsStream(path);
byte[] b = new byte[in.available()];
in.read(b);
String data = new String(b);
String[] s = data.split("\n");
List<String> a = Arrays.asList(s);
ArrayList<String> m = new ArrayList<>(a);
return m;
}
There are two very useful utilities both called JarScan:
www.inetfeedback.com/jarscan
jarscan.dev.java.net
See also this question: JarScan, scan all JAR files in all subfolders for specific class
The most robust mechanism for listing all resources in the classpath is currently to use this pattern with ClassGraph, because it handles the widest possible array of classpath specification mechanisms, including the new JPMS module system. (I am the author of ClassGraph.)
How to know the name of the JAR file where my main class lives?
URI mainClasspathElementURI;
try (ScanResult scanResult = new ClassGraph().whitelistPackages("x.y.z")
.enableClassInfo().scan()) {
mainClasspathElementURI =
scanResult.getClassInfo("x.y.z.MainClass").getClasspathElementURI();
}
How can I read the contents of a directory in a similar fashion within a JAR file?
List<String> classpathElementResourcePaths;
try (ScanResult scanResult = new ClassGraph().overrideClasspath(mainClasspathElementURI)
.scan()) {
classpathElementResourcePaths = scanResult.getAllResources().getPaths();
}
There are lots of other ways to deal with resources too.
One more for the road that's a bit more flexible for matching specific filenames because it uses wildcard globbing. In a functional style this could resemble:
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.function.Consumer;
import static java.nio.file.FileSystems.getDefault;
import static java.nio.file.FileSystems.newFileSystem;
import static java.util.Collections.emptyMap;
/**
* Responsible for finding file resources.
*/
public class ResourceWalker {
/**
* Globbing pattern to match font names.
*/
public static final String GLOB_FONTS = "**.{ttf,otf}";
/**
* #param directory The root directory to scan for files matching the glob.
* #param c The consumer function to call for each matching path
* found.
* #throws URISyntaxException Could not convert the resource to a URI.
* #throws IOException Could not walk the tree.
*/
public static void walk(
final String directory, final String glob, final Consumer<Path> c )
throws URISyntaxException, IOException {
final var resource = ResourceWalker.class.getResource( directory );
final var matcher = getDefault().getPathMatcher( "glob:" + glob );
if( resource != null ) {
final var uri = resource.toURI();
final Path path;
FileSystem fs = null;
if( "jar".equals( uri.getScheme() ) ) {
fs = newFileSystem( uri, emptyMap() );
path = fs.getPath( directory );
}
else {
path = Paths.get( uri );
}
try( final var walk = Files.walk( path, 10 ) ) {
for( final var it = walk.iterator(); it.hasNext(); ) {
final Path p = it.next();
if( matcher.matches( p ) ) {
c.accept( p );
}
}
} finally {
if( fs != null ) { fs.close(); }
}
}
}
}
Consider parameterizing the file extensions, left an exercise for the reader.
Be careful with Files.walk. According to the documentation:
This method must be used within a try-with-resources statement or similar control structure to ensure that the stream's open directories are closed promptly after the stream's operations have completed.
Likewise, newFileSystem must be closed, but not before the walker has had a chance to visit the file system paths.
Just a different way of listing/reading files from a jar URL and it does it recursively for nested jars
https://gist.github.com/trung/2cd90faab7f75b3bcbaa
URL urlResource = Thead.currentThread().getContextClassLoader().getResource("foo");
JarReader.read(urlResource, new InputStreamCallback() {
#Override
public void onFile(String name, InputStream is) throws IOException {
// got file name and content stream
}
});

Categories