I'm looking to add a product version number to appear in my .jar file information. Currently I'm using Maven in my Spring boot project for API Rest.
I have read a lot of solutions about the manifest versioning. There you have to decompress and access to the META-INF/MANIFEST.MF to check the Implementation-Version. That's too tedious for what I'm looking for.
Like for a .exe. where you can found it under right mouse click -> details -> "product version" or simply checking on File Version column as shown on image. Example of a file version description.
Also I read that JAR file is a file format based on the popular ZIP file format and is used for aggregating many files into one. Kinda that I'm looking to add a file version to .zip, but I want to ask anyway if that is possible.
Regards, Gaspar.
A JAR file itself can't have a version. But you're using Maven, and that means you can already access the Maven version:
try (InputStream inputStream = getClass().getResourceAsStream("/META-INF/maven/<groupId>/<artifactId>/pom.properties")) {
Properties properties = new Properties();
properties.load(inputStream);
// available properties:
// - artifactId=xxx
// - groupId=xxx
// - version=xxx
}
Note that this often doesn't work in unit tests (especially when run from IDEs) because the files are only added to the JAR file.
Just include a text file anywhere in your jar. Build systems of all stripes make this trivial. Then write in your code that you read out this text file with the version, and show it on screen.
In maven, gradle, etc, to include a plain text file in the jar, just put it in src/main/resources/version.txt and it gets included automatically.
To read it in java:
public class Main {
public static String getVersion() {
try (var in = Main.class.getResourceAsStream("/version.txt")) {
return new String(in.readAllBytes(), StandardCharsets.UTF_8);
}
}
}
This:
Asks the classloader to load version.txt using the same systems that load .class files.
reads that inputstream fully, and turns it into a string using the UTF_8 encoding.
Uses try-with-resources because, it's a resource, you have to do that if you don't want leaks.
Related
I want read variable value from another jar
Main.jar
public static int version = 2;
I already did add libraries navigate to Main.jar (lib/Main.jar)
then I do this from Loader.jar
int version = dummy.Main.version;
Do replace with a new one if there are update
Loader.jar
URL url = new URL("http://127.0.0.1/cn/Main.jar");
try (InputStream in = url.openStream()) {
Files.copy(in, Paths.get("lib/Main.jar"), StandardCopyOption.REPLACE_EXISTING);
}
catch (HeadlessException | IOException | NumberFormatException e) {
//do exception
}
}
But the problem is i can not replace file because the file is being used, since Main.jar is used by Loader.jar
How the solution to replace the file being used ?
Don't replace the JAR file, because you cannot safely force the other process to not be using it.
Instead, install the newer JAR file next to it, with the version number embedded in the JAR file name, and rewrite your other users of the JAR file to periodically scan for newer JAR files. When finding one, they should throw away the class loaders that loaded the old JAR files, and reload the new set.
Or, decide not to embed this information in a JAR file at all. Resource files are easier to read and dispose of than JAR files, and they are plain text files.
You have some options
close the program using it and try again after restarting.
create a new JAR file each time and load it in a different class loader each time.
read the contents of the class in the JAR using a byte code reader such as ASM.
instead of storing the version in code, store it in a file (or as well). You can easily read the file from a JAR without putting it on the class path.
run javap to dump the code for the static field and extract the value from the text.
I suspect the last option is easiest to code.
I am new to maven and Java . I am working on a large codebase which uses the Spring web framework. I may be missing fundamentals but I have done my best to go over all the basics of the project implementation.
For the feature I am building,I have properties files that I had earlier saved in src/main/resources in
my maven project and was reading from my class named ReaderClass with this statement
ReaderClass.class.getResourceasStream("xyz.properties");
Now I have externalized these files into a separate project and have built a jar out of it. This jar only has the properties files under a folder named resource.
I have added this jar file as an dependency in the IntelliJ IDE and would like to read the properties files from this jar. Had it been a .class file I would use an import statement in ReaderClass but how would I read properties files?
ADDITIONAL INFORMATION
Also, I am not sure if this is a problem but IntelliJ doesn't actually show the jar in the External Libraries Tab but does show my jar in the dependencies tab of the Modules Section in Project Structure. I wanted to make sure this wouldn't affect the solution.
If you are using Spring, try
#Value("${property.name}")
private String property;
to read properties from resources folder.
There are numerous ways to do this.
Assuming your xyz.properties file looks like this:
a=b
y=z
mykey=myvalue
Using java.util.ResourceBundle
If you don't need to pass around the read data as a Properties object, this is the easiest way.
ResourceBundle rb = ResourceBundle.getBundle("xyz");
System.out.println("a=" + rb.getString("a"));
Using java.util.Properties
A bit more work and some boiler-plate, but accomplishes the same thing.
Properties p = new Properties();
try (InputStream is = getClass().getClassLoader().getResourceAsStream("xyz.properties")) {
p.load(is);
}
catch (IOException e) {
// Handle as appropriately.
}
System.out.println("mykey=" + p.getProperty("mykey"));
I have a Swing GUI application. I'd like to have a traditional "About" dialog box that displays some copyright information and the application version to the user.
Our build system (based on Gradle) already keeps track of versions, and there is a property file in the source tree containing the major+minor+patch version number.
It would be fairly easy to have Gradle generate a text file (or use the existing properties file), which is installed with the app. The app could read from the file when displaying the About box. I'd like to avoid that, as I don't like the idea of users being able to modify that file.
Maybe the build could hard-code the version somehow into the built Jar?
Or maybe my app could read the data from the Jar files' Manifest somehow? Updating the MANIFEST.MF is easy in Gradle.
I think lots of other must have solved this well before, but I couldn't find anything on Google or here on StackOverflow which solves this for my particular situation. Ideally, I'm looking for consensus on a best practice
It would be easy to use ClassLoader.getResourceAsStream("/META-INF/MANIFEST.MF") to open the manifest file and then parse out what you need. This would work in or out of the jar file.
Just an idea, but...
During build process you could run an external shellscript to do a replacement in one of your classes' source code. It would take the version as an argument (from Gradle) and hardcode it in your sources. Then, it will get compiled with it.
Should not be too difficult to write. Hope that helps.
Since I want to display a custom attribute (CopyrightYear) I couldn't use the Package API's method to automatically get attributes of the current Jar.
Getting the right data into my jar file's Manifest was the easy part, using Gradle:
jar {
manifest {
attributes("Version": project.version, "CopyrightYear": new Date().format("yyyy"))
}
}
And the java code to retrieve the attributes from the same is adapted from this answer:
private Attributes getMyManifestAttributes() throws IOException {
String className = getClass().getSimpleName() + ".class";
String classPath = getClass().getResource(className).toString();
if (!classPath.startsWith("jar")) {
throw new IOException("I don't live in a jar file");
}
URL url = new URL(classPath);
JarURLConnection jarConnection = (JarURLConnection) url.openConnection();
Manifest manifest = jarConnection.getManifest();
return manifest.getMainAttributes();
}
Using the ClassLoader to locate the manifest as a Resource turned out to deliver me the Manifest files of all my dependency jars, but not of my own one. Go figure. Moreover, it needed more code, to iterate over various resources.
I have written some code and exported it as a jar file. In this jar there is a file named automation.properties with defaults that I'm loading using
val automationPropertiesFileURL = getClass.getResource("/automation.properties")
if (automationPropertiesFileURL != null) {
val source = Source.fromURL(automationPropertiesFileURL)
config = new Properties()
config.load(source.bufferedReader())
}
But when this jar file gets added as a gradle dependency in C:\User\abc\.gradle and I want to read automation.properties from my current project, how can I override the location and read the file from my project and not from the jar file itself?
The class loader will load the file from the location it finds first.
In your case, the file exists in two places:
Inside the current project itself
Inside a jar dependency
Which file will be found by the class loader,
depends on the ordering of the "current project" and the jar dependency on the classpath.
That's what you need to review,
that's the key to loading the right file.
Your current code is correct as it is,
this is a matter of classpath configuration.
I think
Source.fromInputStream(
getClass.getClassLoader.getResourceAsStream("/automation.properties")
)
should work.
API Docs
Source#fromInputStream
Class#getClassLoader
ClassLoader#getResourceAsStream
Java and so does SCALA, have different ways of reading properties file, in one of my answers I explain the difference between reading from properties file inside the Jar and properties file in a disk location.
See my answer HERE:
Loading Properties from a JAR file (java 1.6)
This will work for JAVA and for SCALA too! (Note, for SCALA you could change basic sintax but same concept)
Hope it helps!
I have created a Java program to compare scripts saved as files in the version management tool to those loaded in our database. It's a simple program, runs through start to finish and outputs to the console when it finds a discrepancy. Now I want to load the database URL, username and password as well as the location of my files from a .properties file.
I did assume that if I put the file on the classpath it would be visible from my Java program:
Properties values = new Properties();
try
{
File checkPackages = new File("myfile.properties");
if(!checkPackages.exists()) throw new FileNotFoundException();
values.load(new FileReader(checkPackages));
}
catch(FileNotFoundException fnfe) {}
I also wanted to save this whole program to a .jar file so that it would be that bit more usable. Unfortunately, the only way I have found to reference the .properties file is to have it in the directory where I am running java.exe. The PATH or the CLASSPATH don't seem to apply??
I found an Oracle site about the .jar file's Manifest file as I was hoping there'd be an answer there, but the Class-path: element in the manifest only seems to refer to .jar files that are not in the .jar (and not .properties files that are!)
Questions:
Is there any way to wrap the .properties file into the .jar file so that my user doesn't have to know it is there?
Is there any way to wrap the Oracle driver .jar into the app's .jar so my user doesn't have to know it is there (Oracle says this needs 'custom code')?
TIA
You can get the resources in your classpath (even when sealed in the JAR) by using the ClassLoader#getResource() and ClassLoader#getResourceAsStream() methods.
For example:
Properties values = new Properties();
values.load(ThisClass.class.getClassLoader().getResourceAsStream("myproject.properties"));
// umm, don't forget to close the stream, this code is just an example usage
Note that storing the username and password to any database in a program is considered a heavy security risk.
One of the appraoch can be to use the -D switch to define a system property on a java command line. That system property may contain a path to your properties file.
E.g
java -cp ... -Dmyproject.properties=/path/to/my.app.properties
my.package.App
Fetch the property in your code as mentioned here:
String propPath = System.getProperty( "myproject.properties" );
final Properties myProps;
final FileInputStream in = new FileInputStream( propPath );
try
{
myProps = Properties.load( in );
}
finally
{
in.close( );
}
Well, I would recommend to encrypt the sensitive data (username, password, url in this case) with public and private keys rather than hiding it. It is afterall not hard to deflate any jar file (which is essentially a zip format) and trace the .properties file