I’m creating an executable JAR that will read in a set of properties at runtime from a file. The directory structure will be something like:
/some/dirs/executable.jar
/some/dirs/executable.properties
Is there a way of setting the property loader class in the executable.jar file to load the properties from the directory that the jar is in, rather than hard-coding the directory.
I don't want to put the properties in the jar itself as the properties file needs to be configurable.
Why not just pass the properties file as an argument to your main method? That way you can load the properties as follows:
public static void main(String[] args) throws IOException {
Properties props = new Properties();
props.load(new BufferedReader(new FileReader(args[0])));
System.setProperties(props);
}
The alternative: If you want to get the current directory of your jar file you need to do something nasty like:
CodeSource codeSource = MyClass.class.getProtectionDomain().getCodeSource();
File jarFile = new File(codeSource.getLocation().toURI().getPath());
File jarDir = jarFile.getParentFile();
if (jarDir != null && jarDir.isDirectory()) {
File propFile = new File(jarDir, "myFile.properties");
}
... where MyClass is a class from within your jar file. It's not something I'd recommend though - What if your app has multiple MyClass instances on the classpath in different jar files (each jar in a different directory)? i.e. You can never really guarantee that MyClass was loaded from the jar you think it was.
public static void loadJarCongFile(Class Utilclass )
{
try{
String path= Utilclass.getResource("").getPath();
path=path.substring(6,path.length()-1);
path=path.split("!")[0];
System.out.println(path);
JarFile jarFile = new JarFile(path);
final Enumeration<JarEntry> entries = jarFile.entries();
while (entries.hasMoreElements()) {
final JarEntry entry = entries.nextElement();
if (entry.getName().contains(".properties")) {
System.out.println("Jar File Property File: " + entry.getName());
JarEntry fileEntry = jarFile.getJarEntry(entry.getName());
InputStream input = jarFile.getInputStream(fileEntry);
setSystemvariable(input);
InputStreamReader isr = new InputStreamReader(input);
BufferedReader reader = new BufferedReader(isr);
String line;
while ((line = reader.readLine()) != null) {
System.out.println("Jar file"+line);
}
reader.close();
}
}
}
catch (Exception e)
{
System.out.println("Jar file reading Error");
}
}
public static void setSystemvariable(InputStream input)
{
Properties tmp1 = new Properties();
try {
tmp1.load(input);
for (Object element : tmp1.keySet()) {
System.setProperty(element.toString().trim(),
tmp1.getProperty(element.toString().trim()).trim());
}
} catch (IOException e) {
System.out.println("setSystemvariable method failure");
}
}
Related
I have a global.properties file and have to define the file path inside this properties file.
SheetPath=C:\\Users\\test\\Automation-Scripts\\DataTable.xlsx
This is an absolute path but require a way to define a relative path that can be consumed while calling.
Properties file:
testPath=API_Files/duplicateToken.json
Load the properties file:
public static Properties readProperties = new Properties();
public static void loadPropertiesFile() {
File propertiesFile = new File(location of properties file);
try {
FileInputStream fileInput = new FileInputStream(propertiesFile);
readProperties.load(fileInput);
} catch (Exception e) {
Logger.LogError("Error in loading the Properties file" + e.getMessage());
}
}
Read the properties file and get absolute path:
String testPath = readProperties.getProperty("testPath").trim();
File absolutePath = new File(System.getProperty("user.dir") + testPath);
System.out.println(absolutePath);
Sample output:
C:\Users\test\Automation-Scripts\duplicateToken.json
The properties file should be relative to classpath. You can create a "configs" folder at the level of src and use the below code to read the file. Refer this for more explanation and techniques.
private Properties properties;
private final String propertyFilePath= "configs//Configuration.properties";
BufferedReader reader = new BufferedReader(new FileReader(propertyFilePath));
Properties properties = new Properties();
properties.load(reader);
I'm executing a jar file which reads configs from a config file outside of /home/user/xxx/testFolder/jarfile, the path of config file is /opt/xxx/conf/global_config.cfg.
However, I'm able to access files inside the jar, so I assume the error is due to the file not being found.
Below is my code:
public Properties createProperties(){
Properties p = null;
ClassLoader cl = this.getClass().getClassLoader();
try (InputStream stream = cl.getResourceAsStream("/opt/xxx/conf/global_config.cfg")) {
p = new Properties();
BufferedInputStream bis = new BufferedInputStream(stream);
p.load(bis); // this is throwing the error
System.out.println(p.toString());
} catch (IOException e) {
e.printStackTrace();
}
return p;
}
What is the correct way of getting a file regardless of its path in a Linux system?
cl.getResourceAsStream("/opt/xxx/conf/global_config.cfg")
expects the resource to be available in relation to the class location. So, it will search as a relative path to the class inside the JAR. But the path /opt/xxx/conf/global_config.cfg is a absolute disk path, and for reading it , you need to use the FileInputStream
public Properties createProperties(){
Properties p = null;
ClassLoader cl = this.getClass().getClassLoader();
try (InputStream stream =new FileInputStream("/opt/xxx/conf/global_config.cfg")) {
p = new Properties();
p.load(stream);
System.out.println(p.toString());
} catch (IOException e) {
e.printStackTrace();
}
return p;
}
I have a custom java server. It uses an external xml config file.
I have some command line options to help the user, the usual stuff for showing a help file, setting ports, etc...
I've recently added a command to generate a default config file for the server. It's an xml file. After researching my options, packing a default xml file in the jar seemed to be the way to go, but I'm obviously missing something.
So far my code looks like this:
public class ResourceLoader {
private File outFile = null;
private Reader fileReader = null;
private Writer fileWriter = null;
private InputStream is = null;
private char[] buffer = null;
public ResourceLoader() {
outFile = new File("default-server.xml");
}
public void generateDefaultServerXml() {
is = ResourceLoader.class.getResourceAsStream("/default-server.xml");
if (is == null) {
System.out.println("Configuraiton File generation failed. The InputStream is null.");
} else {
fileReader = new InputStreamReader(is);
}
buffer = new char[4096];
FileOutputStream fos;
try {
fos = new FileOutputStream(outFile);
fileWriter = new OutputStreamWriter(fos);
while (fileReader.read(buffer) != -1) {
fileWriter.write(buffer, 0, buffer.length);
fileWriter.flush();
buffer = new char[4096];
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
fileReader.close();
fileWriter.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
The code above works perfectly fine when I run it in eclipse, but intitially, after I export the jar file the server could not locate the default-server.xml file when I run the command from the terminal.
The file itself is located in a package called main.resources along with some other config files and the above class.
I have since moved the ResourceLoader.class to another package. After doing that the server seems to find the xml file in the main.resources package (InputStream is not null) but the resulting generated default-server.xml file is empty.
Again, this all works perfectly well when I run it in eclipse, it's only after I export the project and try issue the command from the terminal that the process fails. What am I doing wrong?
The above class is instantiated, and the generateDefaultServerXml() is called, from the main method of the server.
EDIT: My path for writing default-server.xml was slightly wrong. Now that I've adjusted it the code works exactly as expected when I run it in Eclipse. The resource is read in the correct way, and written to the file in the correct location. But it still doesn't work when I try the same thing from the jar file.
You current line ResourceLoader.class.getResourceAsStream("/default-server.xml") means that you are trying to load a resource named default-server.xml from the root of your classpath, or put simpler, from the root of your jar file. This means that xml file should NOT be in any package inside the jar file.
When you assemble your jar file and then run jar tf my.jar on it, do you see your default-server.xml file? Does it reside in some package or in the root of the jar file?
The problem here is since you are packaging the application as a jar. The procedure to call an external resource is quite different.
You need to have a folder structure as
root
--your jar
--your xml file
Your code shallwork if the application is using an default-server.xml file inside the jar.
Otherwise, Replace below line in your code if you want to use an external default xml file.
is = new FileInputStream("./default-server.xml");
If the output file you want at root location the use below code
public ResourceLoader() {
outFile = new File("./default-server.xml");
}
Alternate code as per discussion
public class ResourceLoader {
public void generateDefaultServerXml() {
try {
String defaultxmltext =readFileToString("/default-server.xml");
writeFileFromInputString(defaultxmltext);
} catch (IOException e) {
//exception
}
}
public static void writeFileFromInputString(String everything) throws IOException {
try (BufferedWriter writer = new BufferedWriter(new FileWriter("./default-server.xml"))) {
everything = everything.replaceAll("\n", System.getProperty("line.separator"));
writer.write(everything);
}
}
public static String readFileToString(String path) throws IOException {
String everything = null;
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
StringBuilder sb = new StringBuilder();
String line = br.readLine();
while (line != null) {
sb.append(line);
sb.append(System.lineSeparator());
line = br.readLine();
}
everything = sb.toString();
}
return everything;
}
}
Hope this helps
consider your file located on src/main/resources try this
getClass().getClassLoader().getResource(fileName)
well as far as i can see your main problem is that you are passing the wrong path, since you mentioned the xml is under main.resources you will need to add this to the path when trying to load the file, here is a sample piece of code that should work for you
Scanner sc = null;
PrintWriter writer = null;
try {
sc = new Scanner(getClass().getResourceAsStream("main/resources/server.xml"));
writer = new PrintWriter("./default_server.xml", "UTF-8");
while(sc.hasNextLine()) {
writer.println(sc.nextLine());
}
} catch (Exception e) {
} finally {
if(sc != null) {
sc.close();
}
if(writer != null){
writer.close();
}
}
I have a file named InputFile.txt in a resources folder.
My project structure is like this:
VirtualMemory
src
resources
InputFile.txt
VirtualMemory
VirtualMemory.java
And I am trying to access the InputFile.txt in VirtualMemory.java class by like this:
String filename = ("./src/resources/InputFile.txt");
File file = new File(filename);
But the file is not being found. How to resolve this problem?
Below code will help load a properties file from any where in the classpath.
ClassLoader cl = ClassLoader.getSystemClassLoader();
if (cl != null) {
URL url = cl.getResource(CONF_PROPERTIES);
if (url == null) {
url = cl.getResource("/" + CONF_PROPERTIES);
}
if (url != null) {
try {
InputStream in = url.openStream();
props = new Properties();
props.load(in);
} catch (IOException e) {
// Log the exception
} finally {
// close opened resources
}
}
}
the URLClassLoader class fails to load classes from the jar created programatically using the code listed below, whereas when i create a jar with the same classes using jar cf %jarname% %sources% it works fine. Is there a difference between the jars created using jar cf and JarOutputStream.
public static ByteArrayInputStream createJar(File file) throws IOException {
Manifest manifest = new Manifest();
manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
JarOutputStream target = new JarOutputStream(bytes, manifest);
for (File child : file.listFiles()) {
addJarEntries(child, target, "");
}
target.flush();
target.close();
return new ByteArrayInputStream(bytes.toByteArray());
}
private static void addJarEntries(File source, JarOutputStream target, String path) throws IOException {
BufferedInputStream in = null;
try
{
if (source.isDirectory())
{
String name = path +source.getName() + File.separator;
for (File nestedFile: source.listFiles())
addJarEntries(nestedFile, target, name);
return;
}
in = new BufferedInputStream(new FileInputStream(source));
JarEntry entry = new JarEntry(path + source.getName());
entry.setTime(source.lastModified());
target.putNextEntry(entry);
while (true)
{
int count = in.read(buffer);
if (count == -1)
break;
target.write(buffer, 0, count);
}
target.closeEntry();
}
finally
{
if (in != null)
in.close();
}
}
Best Regards,
Keshav
The jar command uses JarOutputStream to create a JAR file (source code) so it cannot be the fault of that class per se. However, it is possible that you missed some important step in the JAR creation process. For instance, you may have included a malformed manifest.
You should be able to compare your code with the source code of the jar command to see if you have missed something important.