Read specific property from a property file in a jar - java

I need to read the property "product.build.number" from the property file "version.properties" which lies at the root level of each of the jars. My naive approach is:
private static int getProductBuildNumber(File artefactFile) throws FileNotFoundException, IOException
{
try (ZipInputStream zip = new ZipInputStream(new FileInputStream(
artefactFile)))
{
Set<String> possClasses = new HashSet<>();
for (ZipEntry entry = zip.getNextEntry(); entry != null; entry = zip
.getNextEntry())
{
if (!entry.isDirectory() && entry.getName().toLowerCase().equals(
"version.properties"))
{
List<String> lines = IOUtils.readLines(zip, (String) null);
for (String line : lines)
{
if (line.startsWith("product.build.number"))
{
String[] split = line.split("=");
if (split.length == 2)
{
return Integer.parseInt(split[1]);
}
}
}
}
}
}
throw new IOException("product.build.number not found.");
}
I guess there are more elegant and reliable ways. Any ideas?

Try something like (untested):
private static int getProductBuildNumber(Path artefactFilePath) throws IOException{
try(FileSystem zipFileSystem = FileSystems.newFileSystem(artefactFilePath, null)){
Path versionPropertiesPath = zipFileSystem.getPath("/version.properties");
Properties versionProperties = new Properties();
try (InputStream is = Files.newInputStream(versionPropertiesPath)){
versionProperties.load(is);
}
return Integer.parseInt(versionProperties.getProperty("product.build.number"));
}
}

You haven’t said whether the .jar files are are in your classpath.
If they are in your classpath, you should be using Class.getResourceAsStream to read the entry:
try (InputStream propStream = getClass().getResourceAsStream("/version.properties")) {
// ...
}
If they .jar files are not in your classpath, you should create a jar: URL from the file. The format of such a URL is described in the JarURLConnection documentation.
Note that java.io.File is obsolete, and you should always use Path instead:
private static int getProductBuildNumber(Path artefactFile)
throws IOException {
URL propsURL = new URL("jar:" + artefactFile.toUri() + "!/version.properties");
try (InputStream propStream = propsURL.openStream()) {
// ...
}
}
Regardless of the data’s location, you should always use the Properties class to read properties. (Parsing a properties file yourself means you have to account for comments, Unicode escapes, continuation lines, and all possible name/value separators.)
Properties props = new Properties();
try (InputStream propStream = getClass().getResourceAsStream("/version.properties")) {
props.load(propStream);
}
int buildNumber = Integer.parseInt(
props.getProperty("product.build.number"));

Related

How to extract and read entries of zip file which is sent as a byte array

In a nutshell I'm trying to write a method which receives the Zip file as byte[] array, and what I want to do is to return the number of entries (the files) that are in the Zip file and test if they're 6 entries.
Here is what I've done so far, which throws FileNotFoundException on line 3
public List<ZipEntry> extractZipEntries(byte[] content) throws IOException {
List<ZipEntry> entries = new ArrayList<>();
ZipFile zip = new ZipFile(content.toString()); //Line 3
for (Enumeration<?> e = zip.entries(); e.hasMoreElements();) {
ZipEntry entry = (ZipEntry) e.nextElement();
entries.add(entry);
}
return entries;
}
test is here:
List<ZipEntry> zipEntries = SomeClassName.extractZipEntries(content);
assertTrue(zipEntries.size() == 6);
Also if possible suggest a better approach, but ideally what I doing above is straight forward here.
Thanks!
Seeing as the source is a byte[], you'll need to use a ByteArrayInputStream to read the file.
public List<ZipEntry> extractZipEntries(byte[] content) throws IOException {
List<ZipEntry> entries = new ArrayList<>();
ZipInputStream zi = null;
try {
zi = new ZipInputStream(new ByteArrayInputStream(content));
ZipEntry zipEntry = null;
while ((zipEntry = zi.getNextEntry()) != null) {
entries.add(zipEntry);
}
} finally {
if (zi != null) {
zi.close();
}
}
return entries;
}

How to delete a specific File/Folder from a jar pragmatically in java

How to delete a specific File/Folder from a jar pragmatically in java.
I have a jar ABC.jar contains files, folder and another jars say child.jar.
under child.jar I want to delete a specific file. How can I do? so that my ABC.jar structure remains same.
Any help will be appreciate.
Thanks in Advance.
As answered by #icza we have to iterate through original jar file and deleting the entry we don't want.
Here is the java code you can refer.
public static void main(String[] args) throws IOException {
String jarName = args[0];
String fileName = args[1];
// Create file descriptors for the jar and a temp jar.
File jarFile = new File(jarName);
File tempJarFile = new File(jarName + ".tmp");
// Open the jar file.
JarFile jar = new JarFile(jarFile);
System.out.println(jarName + " opened.");
// Initialize a flag that will indicate that the jar was updated.
boolean jarUpdated = false;
try {
// Create a temp jar file with no manifest. (The manifest will
// be copied when the entries are copied.)
Manifest jarManifest = jar.getManifest();
JarOutputStream tempJar =
new JarOutputStream(new FileOutputStream(tempJarFile));
// Allocate a buffer for reading entry data.
byte[] buffer = new byte[1024];
int bytesRead;
try {
// Open the given file.
FileInputStream file = new FileInputStream(fileName);
try {
// Create a jar entry and add it to the temp jar.
JarEntry entry = new JarEntry(fileName);
tempJar.putNextEntry(entry);
// Read the file and write it to the jar.
while ((bytesRead = file.read(buffer)) != -1) {
tempJar.write(buffer, 0, bytesRead);
}
System.out.println(entry.getName() + " added.");
}
finally {
file.close();
}
// Loop through the jar entries and add them to the temp jar,
// skipping the entry that was added to the temp jar already.
for (Enumeration entries = jar.entries(); entries.hasMoreElements(); ) {
// Get the next entry.
JarEntry entry = (JarEntry) entries.nextElement();
// If the entry has not been added already, add it.
if (! entry.getName().equals(fileName)) {
// Get an input stream for the entry.
InputStream entryStream = jar.getInputStream(entry);
// Read the entry and write it to the temp jar.
tempJar.putNextEntry(entry);
while ((bytesRead = entryStream.read(buffer)) != -1) {
tempJar.write(buffer, 0, bytesRead);
}
}
}
jarUpdated = true;
}
catch (Exception ex) {
System.out.println(ex);
// Add a stub entry here, so that the jar will close without an
// exception.
tempJar.putNextEntry(new JarEntry("stub"));
}
finally {
tempJar.close();
}
}
finally {
jar.close();
System.out.println(jarName + " closed.");
// If the jar was not updated, delete the temp jar file.
if (! jarUpdated) {
tempJarFile.delete();
}
}
// If the jar was updated, delete the original jar file and rename the
// temp jar file to the original name.
if (jarUpdated) {
jarFile.delete();
tempJarFile.renameTo(jarFile);
System.out.println(jarName + " updated.");
}
}
Sun/Oracle bug database asks for this feature to be implemented in the java api.
Check here
There is a simple way to delete a file from a JAR by executing a command shell during runtime.
Executing the command below does the job:
Runtime.getRuntime().exec("zip -d path\my.jar some_file.txt");
Where path is the absolute path to the jar file and some_file.txt is the file to be deleted. In this example the file resides on the main folder of the jar. You may need to provide its relative path if the file resides on a different folder
The path of the jar itself you may know ahead or can find it relatively based on the class you are executing the command shell:
String path = SomeClass.class.getProtectionDomain().getCodeSource().getLocation().getPath();
You can trace the execution of the process by listening to the available streams:
Process p = Runtime.getRuntime().exec("zip -d path\my.jar some_file.txt");
BufferedReader reader =
new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = "";
StringBuilder sb = new StringBuilder();
while ((line = reader.readLine())!= null) {
sb.append(line + "\n");
System.out.println(line);
}
System.out.println(sb.toString());
Jar/zip files are not editable. You can't delete an entry from a jar/zip file.
What you can do is "re-create" the jar file like this: start a new jar file, iterate over the entries of the current jar file, and add those entries to the new jar file which you do not want to delete.
Theoretically it would be possible to delete an entry like this (but the standard Java lib is not suitable for this, meaning the ZipFile, ZipInputStream, JarFile, JarInputStream classes):
Entries in a jar/zip file are sequencial. Each entry has a header with info about the entry (which may be a file or folder entry). This header also contains the byte-length of the entry. So you could iterate over the entries sequencially, and if you encounter an entry which you want to delete (and you know its size from its header), you could copy the remaining of the file (content after this entry) to the begining of the current entry (obviously the file size should be trunced by the length of the current / deleted entry).
Or your other options include not doing this via Java but using an external tool like the zip -d command itself.
public static void filterJar(Path jarInFileName, String skipRegex, Path jarOutFileName) throws IOException {
ZipEntry entry;
ZipInputStream zis = null;
JarOutputStream os = null;
FileInputStream is = null;
try {
is = new FileInputStream(jarInFileName.toFile());
Pattern pattern = Pattern.compile(skipRegex);
zis = new ZipInputStream(is);
os = new JarOutputStream(new FileOutputStream(jarOutFileName.toFile()));
while ((entry = zis.getNextEntry()) != null) {
if (pattern.matcher(entry.getName()).matches()) continue;
os.putNextEntry(entry);
if (!entry.isDirectory()) {
byte[] bytes = toBytes(zis);
os.write(bytes);
}
}
}
catch (Exception ex) {
throw new IOException("unable to filter jar:" + ex.getMessage());
}
finally {
closeQuietly(is);
closeQuietly(os);
}
}
public static void closeQuietly(final Closeable closeable) {
try {
if (closeable != null) {
closeable.close();
}
}
catch (final Exception e) {}
}
public static byte[] toBytes(InputStream aInput) throws IOException {
byte[] bucket = new byte[5 * 1024];
ByteArrayOutputStream result = null;
result = new ByteArrayOutputStream(bucket.length);
int bytesRead = 0;
while (bytesRead != -1) {
bytesRead = aInput.read(bucket);
if (bytesRead > 0) {
result.write(bucket, 0, bytesRead);
}
}
return result.toByteArray();
}
public static void main(String[] args) throws IOException {
filterJar(Paths.get("./old.jar"), "BOOT-INF/lib.*", Paths.get("./new.jar"));
}
You can use the Zip File System to treat zip/jar files as a file system. This will allow you to edit, delete, and add files to the jar file.
See Appending files to a zip file with Java

Reading filepath from properties file

I'm trying to read a file from a filepath read from properties, but I keep getting FileNotFoundException (the file exists).
test.properties:
test.value = "src/main/resources/File.csv"
LoadProperties.java:
public class LoadProperties {
public static void main(String[] args) throws FileNotFoundException, IOException {
Properties aProp = new Properties();
aProp.load(new FileInputStream("src/main/resources/test.properties")); // works
String filepath = aProp.getProperty("test.value");
System.out.println(filepath); // outputs: "src/main/resources/File.csv"
FileReader aReader = new FileReader("src/main/resources/File.csv"); // works
FileReader aReader2 = new FileReader(filepath); // java.io.FileNotFoundException
}
}
Why is this exception being thrown while the line above it works just fine?
How should I read a file from a path provided with properties?
You are not supposed to put " in your property file. Here Java sees it as :
String file = "\"src/main/resources/File.csv\"";
test.value =src/main/resources/File.csv
You don't need double quotes in properties file to represent a continuous string.
you can write own logic to read properties file, it does not matter whether single quotes or double quotes are there in the file path
String propertyFileLocation = "C:\a\b\c\abc.properties";
try
{
fileInputStream = new FileInputStream(propertyFileLocation);
bufferedReader = new BufferedReader(new InputStreamReader(fileInputStream));
properties = new Properties();
String currentLine = null;
String[] keyValueArray = null;
while ((currentLine = bufferedReader.readLine()) != null) {
if (!currentLine.trim().startsWith("#")) {
keyValueArray = currentLine.split("=");
if (keyValueArray.length > 1) {
properties.put(keyValueArray[0].trim(), keyValueArray[1].trim().replace("\\\\","\\"));
}
}
}
}
catch (Exception e)
{
return null;
}

Trouble getting file path in Netbeans project

I'm making a program that uses text files. I need to make it so that the program can be run from a different computer using its jar file. The problem is that I can't get it to find the right file path to the text files. I've tried using getResource(), but it's still not working right. Here's the code:
public class Params {
public static void init() {
hsChartSuited = new int[13][13];
file = new File(Params.class.getResource("HandStrengthDataSuited.txt").getFile());
try {
Scanner input = new Scanner(file);
for (int i = 0; i < hsChartSuited.length; i++) {
for (int j = 0; j < hsChartSuited[i].length; j++) {
hsChartSuited[i][j] = Integer.parseInt(input.next()) - 20;
}
}
} catch (FileNotFoundException e) {
System.out.println("File not found");
}
}
HandStrengthDataSuited.txt is a file that is in the src folder for my project. It's also located outside of the folder, in the project's main directory as well. I've tried printing the absolute file path, and this is what I get:
/Users/MyUsername/file:/Users/MyUsername/Documents/Homework_Soph_2012/Computer%20Science/HoldEm/dist/HoldEm.jar!/holdem/HandStrengthDataSuited.txt
The file path that I need to get is
/Users/MyUsername/Documents/Homework_Soph_2012/Computer%20Science/HoldEm/holdem/HandStrengthDataSuited.txt
Does anyone know what the problem is here?
If your files is in src folder,
class Tools {
public static InputStream getResourceAsStream(String resource)
throws FileNotFoundException
{
String stripped = resource.startsWith("/") ? resource.substring(1) : resource;
InputStream stream = null;
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if (classLoader != null) {
stream = classLoader.getResourceAsStream(stripped);
}
if (stream == null) {
stream = Tools.class.getResourceAsStream(resource);
}
if (stream == null) {
stream = Tools.class.getClassLoader().getResourceAsStream(stripped);
}
if (stream == null) {
throw new FileNotFoundException("Resource not found: " + resource);
}
return stream;
}
}
Use:
reader = new BufferedReader(new InputStreamReader(getResourceAsStream("org/paulvargas/resources/file.txt"), "UTF-8"));

unzip a zipfile in the same hierachy using java.util.ZipFile

given a zip file with multiple nested directory structure, how do I unzip it into the same tree structure?
does ZipFile.entries() provide the enumeration in any order?
This is mine.
In file you specify the file you want to expand
in target dir you have to specify the target location as "new File("/tmp/foo/bar")". If you want to extract in the current directory you can specify targetDir = new File(".")
public static void unzip(File file, File targetDir) throws ZipException,
IOException {
targetDir.mkdirs();
ZipFile zipFile = new ZipFile(file);
try {
Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
File targetFile = new File(targetDir, entry.getName());
if (entry.isDirectory()) {
targetFile.mkdirs();
} else {
InputStream input = zipFile.getInputStream(entry);
try {
OutputStream output = new FileOutputStream(targetFile);
try {
copy(input, output);
} finally {
output.close();
}
} finally {
input.close();
}
}
}
} finally {
zipFile.close();
}
}
private static void copy(InputStream input, OutputStream output)
throws IOException {
byte[] buffer = new byte[4096];
int size;
while ((size = input.read(buffer)) != -1)
output.write(buffer, 0, size);
}
Worked for me. Good luck.
Here's the one I use all the times. It should directly work after a copy/paste and in any circumstances.
public static File unzip(File inFile, File outFolder)
{ final int BUFFER = 2048;
try
{
BufferedOutputStream out = null;
ZipInputStream in = new ZipInputStream(
new BufferedInputStream(
new FileInputStream(inFile)));
ZipEntry entry;
while((entry = in.getNextEntry()) != null)
{
//System.out.println("Extracting: " + entry);
int count;
byte data[] = new byte[BUFFER];
//We will try to reconstruct the entry directories
File entrySupposedPath = new File(outFolder.getAbsolutePath()+File.separator+entry.getName());
//Does the parent folder exist?
if (!entrySupposedPath.getParentFile().exists()){
entrySupposedPath.getParentFile().mkdirs();
}
// write the files to the disk
out = new BufferedOutputStream(
new FileOutputStream(outFolder.getPath() + "/" + entry.getName()),BUFFER);
while ((count = in.read(data,0,BUFFER)) != -1)
{
out.write(data,0,count);
}
out.flush();
out.close();
}
in.close();
return outFolder;
}
catch(Exception e)
{
e.printStackTrace();
return inFile;
}
}
Zip doesn't offer directory structure per se. The tree alike structure is built by having full path of each entry. ZipFile enumerates the entries in the same way they have been added to the file.
Note: java.util.ZipEntry.isDirectory() just tests if the last character of the name is '/', that's how it works.
What you need to extract the files into the same directory. Parse then name like that:
for(ZipEntry zipEntry : java.util.Collections.list(zipFile.entries())){//lazislav
String name = zipEntry.getName();
int idx = name.lastIndexOf('/');
if (idx>=0) name=name.substring(idx)
if (name.length()==0) continue;
File f = new File(targetDir, name);
}
That shall do it more or less (you still need to take care of duplicate file names, etc)
ZipFile zipFile = new ZipFile("archive.zip");
try {
for (Enumeration<? extends ZipEntry> entries = zipFile.entries(); entries.hasMoreElements();) {
ZipEntry entry = entries.nextElement();
if (entry.isDirectory()) {
new File(entry.getName()).mkdirs();
} else {
InputStream in = zipFile.getInputStream(entry);
try {
OutputStream out = new BufferedOutputStream(new FileOutputStream(entry.getName()));
try {
// this util class is taken from apache commons io (see http://commons.apache.org/io/)
IOUtils.copy(in, out);
} finally {
out.close();
}
} finally {
in.close();
}
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
zipFile.close();
}
Why do you care about order?
If the ZipFile entry has a name /a/b/c/file.txt, then you can work out the directory name /a/b/c and then create a directory in your tree called a/b/c.

Categories

Resources