Maven project - load file from outside of resources folder (project root) - java

I am creating a contacts application using Maven in Netbeans. For the operation of the program I want users to add and store images (contact avatars) in a folder /avatars and access them on a listener event. I can access images from within the ProjectRoot/src/main/resources/images directory, but cannot access ProjectRoot/avatars. Note: I do not want to store avatars in the resources directory because these will be user-added during the programs operation.
I have tried using getClass().getResource(avatarPath); as suggested in similar questions, but it has not worked. I have also tried adding the "avatars" directory to the POM as its own resource directory, but that has not worked either.
How can I access files/folders in the root directory of my project when using Maven?
listviewContacts.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Contact>() {
#Override
public void changed(ObservableValue<? extends Contact> observable, Contact oldValue, Contact newValue) {
String avatar = newValue.getAvatar();
String avatarPath = null;
if (avatar.isEmpty()) {
avatarPath = "/images/" + DEFAULT_AVATAR; // loads from ProjectRoot/src/main/resources/images
} else {
avatarPath = "/avatars/" + avatar; // won't load from ProjectRoot/avatars
}
try {
imageviewContact.setImage(new Image(avatarPath));
} catch (IllegalArgumentException ex) {
System.err.println("Could not locate " + avatarPath);
}
}
});

You are mixing 2 different things.
You can either have a classpath resource packed in jarfile along with your classes, or in a directory that is explicitly added java to java classpath(using java -cp commandline argument). That can be accessed via getClass().getResource.
Or you can load a file from arbitrary location, using java.io.File. Then your "projectRoot" is some folder in a filesystem, that you can either hardcode, configure by -DprojectRoot=C:/fun/with/files, or use some of relative paths.
Maven has nothing to do with it, as avatars "will be will be user-added during the programs operation".
Your users will not launch your IDE, right ?

The problem was in understanding where the different constructors of javafx.scene.image.Image begin their path.
When using the URL constructor for Image, the URL path will start in Maven's defined resources folder (/src/main/resources or whatever additional resource directories were added to the pom.xml file):
// the supplied string invokes the URL constructor
Image image = new Image("path/to/file");
When using the InputStream constructor for Image (via FileInputStream), the path will start at the root directory of the project/application:
// the FileInputStream invokes the InputStream constructor
Image image = new Image(new FileInputStream("path/to/file"));

Related

Java JNI GDAL native library error with ClassLoader when redeploying as web application

I'm using GDAL native library (C++ and it is installed in /usr/lib/java/gdal). I found a trick short time ago, to allow Tomcat can load the web application and this library (cannot use System.load() or System.loadLibrary() as all will return error)
Caused by: java.lang.UnsatisfiedLinkError: org.gdal.osr.osrJNI.new_SpatialReference__SWIG_1()J
Then I need to use a trick to add the library path to JVM when application starts:
final Field usrPathsField = ClassLoader.class.getDeclaredField("usr_paths");
usrPathsField.setAccessible(true);
// get array of paths
final String[] paths = (String[]) usrPathsField.get(null);
// check if the path to add is already present
for (String path : paths) {
if (path.equals(pathToAdd)) {
return;
}
}
//add the new path
final String[] newPaths = Arrays.copyOf(paths, paths.length + 1);
newPaths[newPaths.length - 1] = pathToAdd;
usrPathsField.set(null, newPaths);
This works well when the Tomcat starts with application, however, if I redeploy the application, it will return error:
Caused by: java.lang.UnsatisfiedLinkError: Native Library /usr/lib/java/gdal/libgdaljni.so already loaded in another classloader
I could not find any solution in StackOverflow, so I ask here if anyone can give some information. I also cannot change or add library path to environment variable or Tomcat folder, all should be done in Java code only.
So to avoid to add library to Tomcat/lib folder, I copy all the GDAL native folder to a temp directory with time stamp (e.g: /tmp/gdal_native/date.time), then I use the code above normally, except when it checks for the previous path, it will override with the new one.
String tmpTargetNativeFolderPath = "/tmp/gdal_native" + "/" + current date time
int i = 0;
// check if the path to add is already present
for (String path : paths) {
String pathFolder = StringUtils.substringBeforeLast(path, "/");
if (pathFolder.equals("/tmp/gdal_native")) {
// Override the old path with the new one
paths[i] = tmpTargetNativeFolderPath;
usrPathsField.set(null, paths);
return;
}
i++;
}
Then Classloader will load the library from another folder when the web application is redeployed without the error and the usrPathsField only contains one folder path to /tmp/gdal_native/timestamp.

Jxbtrowser retrieve platform specific artefact at runtime

I'm writing an intelij plugin and would like to download the platform specific artefact at runtime.
I've loaded the platform specific jar into a class loader but the ChromiumExtractor cannot access the nested resources when prefixed with "/". So I can access the resource as "chromium-mac.zip" but the library cannot.
I've tried to unzip the nested zipped chromium artefact into the correct directory but this does not leading to a working solution. So now I've been trying to piece together the way the library extracts the artefact but it's rather tedious as the code is obfuscated.
Does the jxbrowser plugin have some support for retrieving the artefact at runtime. Could such support be added (jxbtrowser devs use SO for support questions etc, this is a message to them :D ) ?
Approach taken :
// inside intelij plugin . The plugin has the jxbrowser-6.6.jar
// and license.jar loaded into the classloader. the platform specific
// artefact will be retrieved manual).
val cl = URLClassLoader(arrayOf(URL("file://.../jxbrowser-mac-6.6.jar")), Browser::class.java.classLoader)
val backup = Thread.currentThread().contextClassLoader
try {
Thread.currentThread().contextClassLoader = cl
// can access like this
Thread.currentThread().contextClassLoader.getResource("chromium-mac.zip")
val ce = ChromiumExtractor.create()
// cannot access as resource is retrieved "/chromium-mac.zip" ?
ce.extract(BrowserPreferences.getChromiumDir())
browser = Browser()
} finally {
Thread.currentThread().contextClassLoader = backup
}
The following does the trick, The resource jar had to be in the same class loader as the client jar (as well as the license). It would be nice if JxBrowser added a helper for this that is capable of performing the download and initialising chromium, perhaps taking just a path for a persistent storage directory.
private fun initializeJxBrowser(): Browser {
if(ChromiumExtractor.create().shouldExtract(BrowserPreferences.getChromiumDir())) {
val cl = URLClassLoader(arrayOf(
URL("file:.../license.jar"),
URL("file:.../jxbrowser-mac-6.6.jar"),
URL("file:../jxbrowser-6.6.jar")
))
cl.loadClass("com.teamdev.jxbrowser.chromium.BrowserContext")
.getMethod("defaultContext")
.invoke(null)
}
return Browser()
}

How to add OpenCV lib to Dynamic Web Project

Currently, I am building a Java web project that use Opencv to detect images that are similar. But when I run, I always get this error
java.lang.UnsatisfiedLinkError: Expecting an absolute path of the
library: opencv_java249 java.lang.Runtime.load0(Runtime.java:806)
java.lang.System.load(System.java:1086)
com.hadoop.DriverServlet.doPost(DriverServlet.java:25)
javax.servlet.http.HttpServlet.service(HttpServlet.java:650)
javax.servlet.http.HttpServlet.service(HttpServlet.java:731)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
I also search this problem but still can not find any solutions for my case. even I try this http://examples.javacodegeeks.com/java-basics/java-library-path-what-is-it-and-how-to-use/ to add to java.library path point to opencv-249 jar in eclipse but still not be resolved.
Anyone can help me? Thanks in advance.
To work with opencv you need jar file and binary file.
JAR file can be simply added by local maven repository or any other variant.
Binary file you need to add and load manually.
Something like this:
private static void addLibraryPath(String pathToAdd) throws Exception{
final Field usrPathsField = ClassLoader.class.getDeclaredField("usr_paths");
usrPathsField.setAccessible(true);
//get array of paths
final String[] paths = (String[])usrPathsField.get(null);
//check if the path to add is already present
for(String path : paths) {
if(path.equals(pathToAdd)) {
return;
}
}
//add the new path
final String[] newPaths = Arrays.copyOf(paths, paths.length + 1);
newPaths[newPaths.length-1] = pathToAdd;
usrPathsField.set(null, newPaths);
}
public void init() {
String pathToOpenCvDll = "c:\\opencv\\"; //linux path works too
try {
addLibraryPath(pathToOpenCvDll);
System.loadLibrary("opencv_java320");
} catch (Exception ignored) {
}
}
}
For web project, the lib jar file should be in the WEB-INF/lib dir.
Also make sure the jars in the dir are in the classpath

Update java desktop app

I am trying to develop a module that can update my running Java Desktop App.
The problem is that I have to replace the actual running jar with another jar, all the while displaying an image and a progress bar with the remaining time of the update process.
One solution I thought about is that I can put a jar in my main jar, and when launching the update process, to extract that second jar which will display the image and the progess bar, and also which will replace the old main jar with a new main jar.
My question is if this is possible and how can I do it.
I do not have a lot of experience with java and java packaging so if you have any examples or links, it would be of great help for me.
Thank you very much.
R.
Run this code when press UPDATE button ..
if(Desktop.isDesktopSupported()){
try {
Desktop.getDesktop().open(new File("update.jar"));
System.exit(0);
} catch (IOException ex) {
}
}
This will open update.jar and close main.jar. Now run this code from main class of update.jar
//wait sometime for terminate main.jar
try{
Thread.sleep(5000);
} catch(Exception e){
e.printStackTrace();
}
if(isUpdateVersionAvailable()) { //first check update from database
if(copyMainJarFileFromServer()){ //copy newMain.jar from server and paste
new File("main.jar").delete(); //delete main.jar
rename(new File("newMain.jar")); //rename newMain.jar to main.jar
}
}
boolean isUpdateVersionAvailable() {
//todo
}
boolean copyMainJarFileFromServer() {
//todo
}
void rename(File file){
file.renameTo(new File("main.jar"));
}
You can have a starter jar that checks for updates and launches the app from the main jar.
It will show start logo, an image, that standard java can display at start-up.
The start0er could also be used to restart the app in another interface language.
package starter;
...
public class StarterApp {
public static void main(String[] args) {
String workDir = System.getProperty("user.dir");
Path mainJar = Paths.get(workDir + "...");
Path nextMainJar = Paths.get(workDir + "...");
if (Files.exists(nextMainJar)) {
Files.copy(nextMainJar, mainJar, StandardCopyAction.REPLACE_EXISTING);
}
URLClassLoader classLoader = new URLClassLoader(new URL[] {mainJar.toURL()});
Class<?> appClass = classLoader.find("mainjar.MainApp");
... instantiate the app
}
As you see the main jar must not be loaded from too early, maybe not be on the class path entirely, and hence the use of a separate ClassLoader. The same might probably be done with the main jar on the class path of the starter app, and using Class.forName("mainjar.MainApp"). The Class-Path can be specified in META-INF/MANIFEST.MF.
The secundary jars may reside in a lib/ directory.
For those readers wanting more modular, service oriented, updateable apps, one could make an OSGi application, a container for bundles (=jars), that provide exchangable services and life-time control.

How to launch a resource jar from inside another java app - with Solution

EDITED with solution (below...)
I have a Splash screen that is packaged into it's own jar. It works.
I can call the Splash.jar from inside another java app by:
Desktop.getDesktop().open(new File("/Applications/Eclipse/Splash.jar"));
and it works just fine. But, that's pretty limited. So, I created a res folder in the new app and dragged the Splash.jar into it.
Now, how do I call it/run it from the main class of the new app??
I looked at all the related posts and see no clear approach...
Thanks
SOLUTION:
I found the solution - so simple. First, the credit to avjava.com for their clear and excellent tutorial on doing this ( Run Another Jar from a Jar ). So, now I can run the Splash (or other .jar) just as hoped for.
The answer is here - posted in the form of complete code:
package test;
import java.io.IOException;
public class RuntimeExecTest1 {
public static void main(String[] args) {
try {
System.out.println("TextEdit I hope");
Runtime runTime = Runtime.getRuntime();
Process process = runTime.exec(
"java -jar /your directory/your app.jar");
try {
Thread.sleep(5000); // keep in open 5000ms
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Closing TextEdit, I hope");
process.destroy(); // kill the process of running the .jar
} catch (IOException e) {
e.printStackTrace();
}
}
}
We don't know how your existing Splash Screen works...
Java AWT support for Splash Screen:
If you are using the Java built-in support for splash screens (as specified in SplashScreen Javadoc) then you need to use a command line switch, or better yet, modify your MANIFEST file in order to reference your Splash Screen:
Manifest-Version: 1.0
Main-Class: Test
SplashScreen-Image: filename.gif
I don't know if, for this particular case, you can reference files in a different JAR. In the worst case, you can unpack the existing JAR (they are just ZIP files) and get the image file in order to include it in your own main jar.
Possibly custom Splash:
If your Splash is created using custom code, then you need the documentation about how to load it. At least, you'd need to add Splash.jar to the classpath of your application and, from your app, call the necessary method or load the appropriate resource.
All the resources and classes contained in .jar files that are added to the classpath are available from your app.
You could create a new URLClassLoader with the Splash.jar and then use reflections to execute the main method.
URL[] urls = new URL[] { new URL("res/Splash.jar") };
ClassLoader cl = new URLClassLoader(urls);
Class<?> clazz = cl.loadClass("splash.Main");
Method method = clazz.getMethod("main", String[].class);
method.invoke(null, new String[0]);
add the resource path to your CLASSPATH envoirment variable and you can use it without modifying your existing code
if your running linux
export CLASSPATH=yourpath;
and if your running windows:
from My Computer right click > properties
OR
if you dont want to add it to CLASSPATH ENV variable,
then
java -classpath="your path to jar" yourclass
Why not define Splash.jar as an external jar and go about using all its routines. Much like JDBC.

Categories