I'm using launch4j to wrap an executable jar file in my Windows application, but I need to pass references to some of its libraries in through the JVM arguments. The libraries in question reside in the application install directory, and are always located in the same place, relative to the executable.
I'd like to tell launch4j to use executable-relative paths in the JVM options. I know this information is available at the Windows batch script level, but how do you configure launch4j to fetch it?
Edit for clarification: I'm looking specifically for how to make the paths relative to the binary itself, not how to make them relative to the current working directory. The two aren't necessarily the same.
You might add to your launch4j configuration
...
<jre>
...
<opt>-Djna.library.path="%EXEDIR%\\path\\to\\lib"</opt>
<opt>-Djava.library.path="%EXEDIR%\\path\\to\\lib"</opt>
...
</jre>
...
If you need more then a you might seperate several paths by a semikolon as usual.
< opt> Optional, accepts everything you would normally pass to
java/javaw launcher: assertion options, system properties and X
options. Here you can map environment and special variables EXEDIR
(exe's runtime directory), EXEFILE (exe's runtime full file path) to
system properties. All variable references must be surrounded with
percentage signs and quoted.
Source: http://launch4j.sourceforge.net/docs.html
Set -Djna.library.path=<relative path of native libraries> (if using JNA) and -Djava.library.path=<relative path of native libraries>.
Alternatively, this can be done in Java code as: System.setProperty("jna.library.path","<relative path of native libraries>") and System.setProperty("java.library.path","<relative path of native libraries>"). You can append as many paths to refer to. In Windows, use ; to separate the paths.
This setup only has its effect on the JVM runtime of that Java application (not globally like LD_LIBRARY_PATH in Linux.)
Or, you can put this in Launch4J JVM options list under JRE tab. This is what I do in my projects.
One of the options in configuration is to allow a change directory chdir to the executables directory. This will set user.dir to same directory as exe, which you could use to find other application paths.
<chdir>
Optional. Change current directory to an arbitrary path relative to the executable. If you omit this property or leave it blank it will have no effect.
Setting it to . will change the current dir to the same directory as the executable. .. will change it to the parent directory, and so on.
<chdir>.</chdir>
<chdir>../somedir</chdir>
The code which find the actual path to executable will be dependent on OS (readlink, GetModuleFileName etc). Make sure you really test on target OSes..
If I understand your question correct, you have a launch4j executable and a native library within your installation directory:
/launch.exe
/bin/lib.dll
/lib/app.jar
Now you want to start you app.jar with the generated launcher (launch.exe). You app loads the lib.dll.
You can embed a file into your app.jar (marker.txt). Now you can use the ClassLoader
http://docs.oracle.com/javase/6/docs/api/java/lang/ClassLoader.html#getResource(java.lang.String)
getResource("marker.txt);
This will give you something like:
file://c://installdir/lib/app.jar!marker.txt
This String can be parsed. But in fact, I think there should be a better solution for this problem.
You can simply include the directory (e.g. ..\lib) where the libraries are located in the classpath tab in Launch4j. At least that worked for me.
Related
I have a Java Application which generates a temporary JNA folder and a temporary DLL file to the following location in the users' profile:
%OSDRIVE%\Users\ABC-<SOME-USER-ID>\AppData\Local\Temp\jna--881477353\jna7513918229606912988.dll
(the JNA folder and file names contain random numbers as suffix and with prefix "jna--" , "jna" respectively)
The JNA DLL file "Path" needs to be made an exception in Application Control Policies, specifically in AppLocker -> DLL Rules -> [Users] -> Exceptions in order for the Java app to function properly. At the moment, it is blocked by the AppLocker, however, if I add this path to the AppLocker, it will work for the current user.
Now, this is achievable for 1 user, but I have many users with the prefix as "ABC-" and the suffix "SOME-USER-ID" contains a random string with numbers and letters, i.e. many users who have prefix "ABC-" should be able to use this application without the need for manually adding every users' profile path into the AppLocker configuration.
Is there a way I can "Wildcard" this path into the AppLocker configuration?
For example, something like this:
%OSDRIVE%\Users\ABC-<WILDCARD>\AppData\Local\Temp\jna--<WILDCARD>\jna<WILDCARD>.dll
or even:
%OSDRIVE%\Users\ABC-<WILDCARD>\AppData\Local\Temp\jna--<WILDCARD>\*
Is there a realistic way of achieving this via Wildcards?
I do not wish to use "*" inside the path because it will then allow ALL users to be exempted from the AppLocker settings.
Any help is appreciated.
Thanks in advance!
Based on the available documentation I could find, it appears that a wildcard (*) character is only supported in AppLocker at the beginning or end of a path, but not in the middle. So your proposed solution is impossible.
However, rather than using a wildcard in the path for AppLocker, you can pre-extract the JNA native library to a known location for all users. This is a relatively common need for security purposes (exactly your intent), sometimes related to temp directory access permissions or sometimes related to signing binaries.
From the JNA API Overview Loading JNA:
JNA includes a small, platform-specific shared library which enables all native access. When the Native class is first accessed, JNA will first attempt to load this library from the directories specified in jna.boot.library.path. If that fails and jna.nosys=false is set, it will fall back to loading from the system library paths. Finally it will attempt to extract the stub library from from the JNA jar file, and load it.
This gives you two options to avoid the randomly-named temporary file. Copy it to:
a directory of your choice, and either pass -Djna.boot.library.path=C:\your\path on the java command line, or before loading JNA call System.setProperty("jna.boot.library.path", "C:\your\path") in your program.
a system directory on the PATH, e.g. System32, and set jna.nosys=false. (You can also set jna.nounpack=true to prevent the temp file unpacking.)
In addition, the JNA native library will be in a subdirectory of the Java temporary directory specified by the java.io.tmpdir system property. See this SO question: Environment variable to control java.io.tmpdir?
Answers to that question include using the _JAVA_OPTIONS environment variable as a possible means to change the tmpdir even if you're running an executable rather than Java command line.
I was just reading this line:
The first thing the format() method does is load a Velocity template from the classpath named output.vm
Please explain what was meant by classpath in this context, and how I should set the classpath.
When programming in Java, you make other classes available to the class you are writing by putting something like this at the top of your source file:
import org.javaguy.coolframework.MyClass;
Or sometimes you 'bulk import' stuff by saying:
import org.javaguy.coolframework.*;
So later in your program when you say:
MyClass mine = new MyClass();
The Java Virtual Machine will know where to find your compiled class.
It would be impractical to have the VM look through every folder on your machine, so you have to provide the VM a list of places to look. This is done by putting folder and jar files on your classpath.
Before we talk about how the classpath is set, let's talk about .class files, packages, and .jar files.
First, let's suppose that MyClass is something you built as part of your project, and it is in a directory in your project called output. The .class file would be at output/org/javaguy/coolframework/MyClass.class (along with every other file in that package). In order to get to that file, your path would simply need to contain the folder 'output', not the whole package structure, since your import statement provides all that information to the VM.
Now let's suppose that you bundle CoolFramework up into a .jar file, and put that CoolFramework.jar into a lib directory in your project. You would now need to put lib/CoolFramework.jar into your classpath. The VM will look inside the jar file for the org/javaguy/coolframework part, and find your class.
So, classpaths contain:
JAR files, and
Paths to the top of package hierarchies.
How do you set your classpath?
The first way everyone seems to learn is with environment variables. On a unix machine, you can say something like:
export CLASSPATH=/home/myaccount/myproject/lib/CoolFramework.jar:/home/myaccount/myproject/output/
On a Windows machine you have to go to your environment settings and either add or modify the value that is already there.
The second way is to use the -cp parameter when starting Java, like this:
java -cp "/home/myaccount/myproject/lib/CoolFramework.jar:/home/myaccount/myproject/output/" MyMainClass
A variant of this is the third way which is often done with a .sh or .bat file that calculates the classpath and passes it to Java via the -cp parameter.
There is a "gotcha" with all of the above. On most systems (Linux, Mac OS, UNIX, etc) the colon character (':') is the classpath separator. In windowsm the separator is the semicolon (';')
So what's the best way to do it?
Setting stuff globally via environment variables is bad, generally for the same kinds of reasons that global variables are bad. You change the CLASSPATH environment variable so one program works, and you end up breaking another program.
The -cp is the way to go. I generally make sure my CLASSPATH environment variable is an empty string where I develop, whenever possible, so that I avoid global classpath issues (some tools aren't happy when the global classpath is empty though - I know of two common, mega-thousand dollar licensed J2EE and Java servers that have this kind of issue with their command-line tools).
Think of it as Java's answer to the PATH environment variable - OSes search for EXEs on the PATH, Java searches for classes and packages on the classpath.
The classpath is one of the fundamental concepts in the Java world and it's often misunderstood or not understood at all by java programmes, especially beginners.
Simply put, the classpath is just a set of paths where the java compiler and the JVM must find needed classes to compile or execute other classes.
Let's start with an example, suppose we have a Main.java file thats under C:\Users\HP\Desktop\org\example,
package org.example;
public class Main {
public static void main(String[] args) {
System.out.println("Hello world");
}
}
And Now, suppose we are under C:\ directory and we want to compile our class, Its easy right, just run:
javac .\Users\HP\Desktop\org\example\Main.java
Now for the hard question, we are in the same folder C:\ and we want to run the compiled class.
Despite of what you might think of to be the answer, the right one is:
java -cp .\Users\HP\Desktop org.example.Main
I'll explain why, first of all, the name of the class that we want ro tun is org.exmaple.Main not Main, or Main.class or .\users\hp\desktop\org\example\Main.class ! This is how things works with classes declared under packages.
Now, we provided the name of the class to the JVM (java command in this case), But how it (JVM) will know where to find the .class file for the Main class? Thats where the classpath comes into picture. Using -cp flag (shortcut for -classpath), we tell the JVM that our Main.class file will be located at C:\users\hp\Desktop.. In fact, not really, we tell it to just go to the Desktop directory, and, because of the name of the class org.example.Main, the JVM is smart and it will go from Desktop to org directory, and from org to example directory, searching for Main.class file, and it will find it and it will kill it, I mean, it will run it :D .
Now lets suppose that inside the Main class we want to work with another class named org.apache.commons.lang3.StringUtils and the latter is located in a jar file named commons-lang3-3.10.jar thats inside C:\Users\HP\Downloads. So Main.java will look like this now:
package org.example;
import org.apache.commons.lang3.StringUtils;
public class Main {
public static void main(String[] args) {
System.out.println("Hello world");
System.out.println(StringUtils.equals("java", "java")); //true
}
}
How to compile the Main.java if we are always inside C:\ ? The answer is:
javac -cp .\Users\HP\Downloads\commons-lang3-3.10.jar .\Users\HP\Desktop\org\example\Main.java
.\Users\HP\Desktop\org\example\Main.java is because our .java file is there in the filesystem.
-cp .\Users\HP\Downloads\commons-lang3-3.10.jar is because the java compiler (javac in this case) need to know the location of the class org.apache.commons.lang3.StringUtils, so we provided the path of the jar file, and the compiler will then go inside the jar file and try to find a file StringUtils.class inside a directory org\apache\commons\lang3.
And if we want to run the Main.class file, we will execute:
java -cp ".\Users\HP\Desktop\;.\Users\HP\Downloads\commons-lang3-3.10.jar" org.example.Main
org.example.Main is the name of the class.
".\Users\HP\Desktop\;.\Users\HP\Downloads\commons-lang3-3.10.jar" are the paths (separated by ; in Windows) to the Main and StringUtils classes.
The classpath is the path where the Java Virtual Machine look for user-defined classes, packages and resources in Java programs.
In this context, the format() method load a template file from this path.
The classpath in this context is exactly what it is in the general context: anywhere the VM knows it can find classes to be loaded, and resources as well (such as output.vm in your case).
I'd understand Velocity expects to find a file named output.vm anywhere in "no package". This can be a JAR, regular folder, ... The root of any of the locations in the application's classpath.
Setting the CLASSPATH System Variable
To display the current CLASSPATH variable, use these commands in Windows and UNIX (Bourne shell):
In Windows: C:\> set CLASSPATH
In UNIX: % echo $CLASSPATH
To delete the current contents of the CLASSPATH variable, use these commands:
In Windows: C:\> set CLASSPATH=
In UNIX: % unset CLASSPATH; export CLASSPATH
To set the CLASSPATH variable, use these commands (for example):
In Windows: C:\> set CLASSPATH=C:\users\george\java\classes
In UNIX: % CLASSPATH=/home/george/java/classes; export CLASSPATH
Classpath is an environment variable of system. The setting of this variable is used to provide the root of any package hierarchy to java compiler.
CLASSPATH is an environment variable (i.e., global variables of the operating system available to all the processes) needed for the Java compiler and runtime to locate the Java packages used in a Java program. (Why not call PACKAGEPATH?) This is similar to another environment variable PATH, which is used by the CMD shell to find the executable programs.
CLASSPATH can be set in one of the following ways:
CLASSPATH can be set permanently in the environment: In Windows, choose control panel ⇒ System ⇒ Advanced ⇒ Environment Variables ⇒ choose "System Variables" (for all the users) or "User Variables" (only the currently login user) ⇒ choose "Edit" (if CLASSPATH already exists) or "New" ⇒ Enter "CLASSPATH" as the variable name ⇒ Enter the required directories and JAR files (separated by semicolons) as the value (e.g., ".;c:\javaproject\classes;d:\tomcat\lib\servlet-api.jar"). Take note that you need to include the current working directory (denoted by '.') in the CLASSPATH.
To check the current setting of the CLASSPATH, issue the following command:
> SET CLASSPATH
CLASSPATH can be set temporarily for that particular CMD shell session by issuing the following command:
> SET CLASSPATH=.;c:\javaproject\classes;d:\tomcat\lib\servlet-api.jar
Instead of using the CLASSPATH environment variable, you can also use the command-line option -classpath or -cp of the javac and java commands, for example,
> java –classpath c:\javaproject\classes com.abc.project1.subproject2.MyClass3
For linux users, and to sum up and add to what others have said here, you should know the following:
$CLASSPATH is what Java uses to look through multiple directories to find all the different classes it needs for your script (unless you explicitly tell it otherwise with the -cp override). Using -cp requires that you keep track of all the directories manually and copy-paste that line every time you run the program (not preferable IMO).
The colon (":") character separates the different directories. There is only one $CLASSPATH and it has all the directories in it. So, when you run "export CLASSPATH=...." you want to include the current value "$CLASSPATH" in order to append to it. For example:
export CLASSPATH=.
export CLASSPATH=$CLASSPATH:/usr/share/java/mysql-connector-java-5.1.12.jar
In the first line above, you start CLASSPATH out with just a simple 'dot' which is the path to your current working directory. With that, whenever you run java it will look in the current working directory (the one you're in) for classes. In the second line above, $CLASSPATH grabs the value that you previously entered (.) and appends the path to a mysql dirver. Now, java will look for the driver AND for your classes.
echo $CLASSPATH
is super handy, and what it returns should read like a colon-separated list of all the directories, and .jar files, you want java looking in for the classes it needs.
Tomcat does not use CLASSPATH. Read what to do about that here: https://tomcat.apache.org/tomcat-8.0-doc/class-loader-howto.html
Static member of a class can be called directly without creating object instance.
Since the main method is static Java virtual Machine can call it without creating any instance of a class which contains the main method, which is start point of program.
I wrote a program that works on my laptop perfectly, but I really want it to work on a server that I have. Using NetBeans, I've clean and built the project. I copied the contents of the folder dist on my server but I cannot seem to get to work by using command
java -jar nameOfFile.jar
I get the error
java.lang.NoClassDefFoundError: org/....
I have been doing some reading and from what I gather is that I need to pretty much specify where the libraries that I've used are located. Well they are located in a subfolder called lib.
Question:
So what would I need to do in order to be able to run my jar?
CLASSPATH is an environment variable that helps us to educate the Java Virtual Machine from where it will start searching for .class files.
We should store the root of the package hierarchies in the CLASSPATH environment variables.
In case of adding or using jar libraries in our project, we should put the location of the jar file in the CLASSPATH environment variable.
Example: If we are using jdbc mysql jar file in our java project, We have to update the location of the mysql jar file in the CLASSPATH environment variable. if our mysql.jar is in c:\driver\mysql.jar then
We can set the classpath through DOS in Windows
set CLASSPATH=%CLASSPATH%;c:\driver\mysql.jar
In Linux we can do
export CLASSPATH=$CLASSPATH:[path of the jar]
Hope it helps!
Try that:
java -classpath "$CLASSPATH:nameOfFile.jar:lib/*" path.to.your.MainClass
What this does is setting the classpath to the value of $CLASSPATH, plus nameOfFile.jar, plus all the .jar files in lib/.
Classpath
A compiler(e.g. javac) creates from .java - .class files and JVM uses these .class files.
classpath - local codebase[About] - points on the root of source. classpath + import_path = full path
For example for MacOS
//full path
/Users/Application.jar/my/package/MainClass
//classpath
/Users/Application.jar
//import_path
my.package.MainClass
Android classpath
ANDROID_HOME/platforms/android-<version>/android.jar
//e.g
/Users/alex/Library/Android/sdk/platforms/android-23/android.jar
When you use a META-INF/MANIFEST.MF file to specify the Main-Class dependencies must be specified in the manifest too.
The -jar switch ignores all other classpath information - see the tools docs for more.
You need to set class path using
The below works in bash .
This is temporary
set CLASSPATH=$CLASSPATH=[put the path here for lib]
If you want it permanent then you can add above lines in ~/.bashrc file
export CLASSPATH=$CLASSPATH:[put the path here for lib]:.
You have 2 questions, one is the "title question" and another is the "foot note question" after elaborating your problem.
Read this documentation bellow to get a better understanding of CLASSPATH.
https://docs.oracle.com/javase/tutorial/essential/environment/index.html
https://docs.oracle.com/javase/8/docs/technotes/tools/windows/classpath.html
This is fast and straight forward for what you need.
For your first question, this will do:
The documentation recommends us to set a classpath for every application we are running at the moment using (use in the command-line):
java -classpath C:\yourDirectoryPath myApp
For your second question, look this exercise in the java documentation. It seems to be the same problem:
https://docs.oracle.com/javase/tutorial/essential/environment/QandE/answers.html
Answers to Questions and Exercises: The Platform Environment
Question 1.A programmer installs a new library contained in a .jar file. In order to access the library from his code, he sets the CLASSPATH environment variable to point to the new .jar file. Now he finds that he gets an error message when he tries to launch simple applications:
java Hello
Exception in thread "main" java.lang.NoClassDefFoundError: Hello
In this case, the Hello class is compiled into a .class file in the current directory — yet the java command can't seem to find it. What's going wrong?
Answer 1. A class is only found if it appears in the class path. By default, the class path consists of the current directory. If the CLASSPATH environment variable is set, and doesn't include the current directory, the launcher can no longer find classes in the current directory. The solution is to change the CLASSPATH variable to include the current directory. For example, if the CLASSPATH value is c:\java\newLibrary.jar (Windows) or /home/me/newLibrary.jar (UNIX or Linux) it needs to be changed to .;c:\java\newLibrary.jar or .:/home/me/newLibrary.jar."
I need to be able to read in a property file, that lives outside of my war. My problem is that a need a solution that will allow me to tell my war file where my property file is located. Can this be done through bashrc variables and windows env variables?
I need to do this because I need to be able to drop the property file in different locations that could be away from the war file.
I am struggling to come up with a solution.
This will very much depends what the property file is for. Some libraries will have have the possibility of setting it on the command-line and others will allow you to explicitly load them in code.
In case of the latter, System.getProperties() can prove helpful, since it allows you to read properties passed to the JVM using the '-D' flag. For example
java -jar -Dfilelocation="yourfilelocation" yourapp.jar
would populate the system property 'filelocation' with the string 'yourfilelocation'. This could then be used in your code to load the property file (or whatever you want to do with it).
Since you are running inside some sort of application server, there are different ways you can accomblish this. For jetty you can put them in start.ini (or simply pass them on the command-line when you start jetty)
where tomcat uses an enviroment variable called JAVA_OPTS, so
JAVA_OPTS='-Dfilelocation=yourfilelocation' start.sh
would set the system property when you start tomcat.
one way is to provide specific location by passing java argument or setting up environment varialble and read it from app to determine the location
and as a fallback (default) app should be announcing to look at
${user.home}/appname/some.peroperties
You could defines a list of possible directories ... and try to read each one ...
I have a solution that read a property file in the file system using a default directory structure.
Eg: c:\properties\code-suit\prd\application.properties
Where:
code-suit is a variable defined by application name;
prd is a environment that I will use
Good luck
I have created a Java application that loads some configurations from a file conf.properties which is placed in src/ folder.
When I run this application on Windows, it works perfectly. However when I try to run it on Linux, it throws this error:
java.io.FileNotFoundException: src/conf.properties (No such file or directory)
If you've packaged your application to a jar file, which in turn contains the properties file, you should use the method below. This is the standard way when distributing Java-programs.
URL pUrl = this.getClass().getResource("/path/in/jar/to/file.properties");
Properties p = new Properties();
p.load(pUrl.openStream());
The / in the path points to the root directory in the jar file.
Instead of
String PROP_FILENAME="src/conf.properties";
use
String PROP_FILENAME="src" + File.separator + "conf.properties";
Check the API for more detail: http://java.sun.com/j2se/1.5.0/docs/api/java/io/File.html
I would also check what your current working directory is if your path to that file is relative. You just need to make a File test = new File("."); and then print that files canonical path name.
If you are referencing any other locations like user.dir or something to that effect by using System.getProperty(), you'll want to at least verify that the directory you are using as the relative root is where you think it is.
Also, as Myles noted, check the slashes used as file path separators. Although you can always use the "/" and it works.
And if you are referencing the path absolutely, you'll have trouble going between one OS and another if you do something silly like hard-code the locations.
What you want to do is check out System.getProperties() and look for file.separator. The static File.pathSeprator will also get you there.
This will allow you to build a path that is native for whatever system you're running on.
(If indeed that is the problem. Sometimes I like to get the current directory just to make sure the directory I think I'm running in is the directory I'm really running in.)
Check your permissions. If you (or rather, the user that the Java process is running under) doesn't have appropriate permissions to read the file, for example, you would get this error message.
This is a typical Windows -> Linux migration problem. What does ls -l src/conf.properties show when run from a prompt?
Additionally, check capitalisation. Windows isn't case-sensitive, so if the file was actually called e.g. CONF.properties it would still be found, whereas the two would be considered different files on Linux.
You should check the working directory of your application. Perhaps it is not the one you assume and that's why 'src' directory is not present.
An easy check for this is to try the absolute path (only for debugging!).
I would check your slashes, windows often uses '\' vs linux's '/' for file paths.
EDIT: Since your path looks fine, maybe file permissions or executing path of the app is different?
check your slashes and colons
in my case i set my PS1 to following value
PS1='\n[\e[1;32m]$SYSNAME(\u)#[\e[1;33m]\w [\e[1;36m](\d \T) [!]\e[0m]\n\$ '
i am trying to read from the env .such as system.getenv
Java was throwing exception
java.lang.IllegalArgumentException: Malformed \uxxxx encoding
Try the double slash, after doing things in JBoss I often had to refactor my code to use the double slashes