Let's say I have a main class App that loads all jars in the sub-directory plugins using a URLClassLoader:
public class App(){
public static void main(String[] args){
for(File f : new File("plugins").listFiles()){
URL[] urls = { new URL("jar:file:" + "plugins/" + f.getName() + ".jar" + "!/") };
URLClassLoader cl = URLClassLoader.newInstance(urls);
Class<?> clazz = cl.loadClass(f.getName().toLowerCase()+"."+f.getName());
cl.close();
Plugin p = ((Plugin) clazz.newInstance());
}
}
}
All those jars contain a class that implements an interface Plugin.
+-- Main.jar
| +-- App.class
| +-- Plugin.class
|
+-- Plugins/
| +-- PluginTest.jar
| +-- plugintest
| +-- PluginTest.class
| +-- Two.class
That's all working fine if I write my code just in the PluginTest class. But as soon as I try to access Two from PluginTest, I'm getting a big error:
Exception in thread "Thread-4" java.lang.NoClassDefFoundError: plugintest/Two
[...]
How should I load the the class correctly? Need help! Thanks.
Do not close your classloader.
Remove cl.close();statement.
Related
I have two .aidl files and I need to add them to a project delphi.
IPrinterCallback.aidl
IPrinterService.aidl
// IPrinterService.aidl
package com.xcheng.printerservice;
import com.xcheng.printerservice.IPrinterCallback;
//import java.util.List;
//import java.util.Map;
// Declare any non-default types here with import statements
interface IPrinterService {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
...
...
The idea is simply to create a button to click and print anything using these packages.
I read several tutorials and got nothing so far.
In one of them I saw that I needed to generate the equivalent .java files and add them to the project.
/*
* This file is auto-generated. DO NOT MODIFY.
*/
package com.xcheng.printerservice;
//import java.util.List;
//import java.util.Map;
// Declare any non-default types here with import statements
public interface IPrinterService extends android.os.IInterface
{
/** Default implementation for IPrinterService. */
public static class Default implements com.xcheng.printerservice.IPrinterService
{
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
#Override public void upgradePrinter() throws android.os.RemoteException
{
...
...
...
So I generated them using aidl.exe from skd android and put them in a folder inside the project.
But I always get a compilation error when I try to import it in 'uses'.
my code without importing the AIDL
unit Unit1;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
FMX.Controls.Presentation, FMX.StdCtrls,
Androidapi.JNI.JavaTypes,
Androidapi.JNI.Os,
Android.ServiceApplication,
FMX.Helpers.Android;
type
TForm1 = class(TForm)
Print: TButton;
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.fmx}
end.
MyProject/
|
+-- src/
| |
| +-- com/
| |
| +-- xcheng/
| |
| +-- printerservice/
| |
| +-- IPrinterService.aidl
| +-- IPrinterCallback.aidl
| +-- IPrinterService.java
| +-- IPrinterCallback.java
|
+-- assets/
|
+-- AndroidManifest.xml
+-- MyProject.dproj
...
...
Can anyone help me import and consume these files .aidl or .java in android delphi?
I'm currently trying to call some C++ code from Java using JNI. To understand how this would work, I followed a tutorial. I have had some hiccups, but now I am almost there. So far, I have successfully created the Java Class, implemented a method in C++, compiled the code from my windows cmd, and created the library. The last thing I'm struggling with and cannot seem to figure out is how to correctly refer to the library when running the code from the command line.
My folder structure is as follows
.
├── src
└── main
└── java
└── com
└── baeldung
└── jni
└── com_baeldung_jni_HelloWorldJNI.cpp
└── com_baeldung_jni_HelloWorldJNI.cpp~
└── com_baeldung_jni_HelloWorldJNI.h
└── com_baeldung_jni_HelloWorldJNI.o
└── HelloWorldJNI.class
└── HelloWorldJNI.java
└── native.dll
The general set-up for running the code is:
java -cp . -Djava.library.path=/NATIVE_SHARED_LIB_FOLDER com.baeldung.jni.HelloWorldJNI
First of all, it took me some time to figure out the folder from which I had to call this in cmd, to not get the error
Error: could not find or load main class com.baeldung.jni.HelloWorldJNI
I solved this issue calling the previous line while being in the src/main/java directory. I have tried different options for the library path, but all returned
Exception in thread "main" java.lang.UnsatisfiedLinkError: no native in java.library.path
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1860)
at java.lang.Runtime.loadLibrary0(Runtime.java:870)
at java.lang.System.loadLibrary(System.java:1122)
at com.baeldung.jni.HelloWorldJNI.<clinit>(HelloWorldJNI.java:6)
Any suggestions on how to set the correct relative library path are more than welcome!
Thanks a lot in advance!
Not a direct answer, just some notes what works for me in a similar situation.
My project creates a jar that consists of some java code and a native dll as resource. The following steps are performed so it works in all my use cases:
I have a class that handles resources. The lookup via the classLoader ensures that it also works when packaged in a fat jar.
public class Resources {
private static final Logger LOGGER = Logger.getLogger(Resources.class.getName());
public static <T> InputStream getResourceAsStream(Class<T> clazz, String name) {
final var classLoader = clazz.getClassLoader();
if (classLoader != null) {
final var result = classLoader.getResourceAsStream(name);
if (result != null) {
LOGGER.info("using resource '" + name + "' obtained via the classLoader '" + classLoader.getName() + "'.");
return result;
}
}
LOGGER.info("using resource '" + name + "' obtained via class '" + clazz.getName() + "'.");
return clazz.getResourceAsStream(name);
}
public static <T> void extractResourceToFilesystem(Class<T> clazz, final Path destination, final String resourceFilename) {
if (!Files.isDirectory(destination)) {
throw new IllegalArgumentException("The configuration path '" + destination.toAbsolutePath() + "' must be a directory");
}
if (!Files.isWritable(destination)) {
throw new IllegalArgumentException("The java process must have write permissions in directory '" + destination.toAbsolutePath() + "'");
}
final Path resourcePath = destination.resolve(resourceFilename);
try(final InputStream in = Resources.getResourceAsStream(clazz, "/" + resourceFilename)) {
Files.copy(in, resourcePath, StandardCopyOption.REPLACE_EXISTING);
} catch (IOException ex) {
throw new IllegalArgumentException(ex.getMessage());
}
if (!Files.exists(resourcePath)) {
throw new IllegalArgumentException("The file '" + resourcePath.toAbsolutePath() + "' could not be created.");
}
LOGGER.info("Successfully extracted resource to " + resourcePath.toAbsolutePath());
}
}
The class that uses the native library first writes the dll to the application directory. This directory is always searched from loadLibrary. I use a singleton that is loaded lazily (you could do the initialization in a static block but I need this for reasons that are not important here)
class MyClass {
public native void foo(...);
public native int bar(...);
private MyClass() {
Resources.extractResourceToFilesystem(MyClass.class, Paths.get(""), "myDll.dll");
System.loadLibrary("myDll");
}
private static class LazyHolder {
private final static MyClass INSTANCE = new MyClass();
}
public static MyClass getInstance() {
return LazyHolder.INSTANCE;
}
}
I created 2 random modules I exported "com.alice" package from folder A and require the module in folder B. Then compiled A: javac mod10/A/module-info.java mod10/A/com/alice/Ship.java and tried to compile B javac mod10/B/module-info.java mod10/B/com/alice2/Main.java. And I get this:
error: module not found: com.alice
requires com.alice;
^
1 error
why I get "module not found" error message?
Shouldn't it be the whole point of exporting a module to make it visible to other modules?
In mod10/A/module-info.java
module com.alice {
exports com.alice;
}
Ship.java
package com.alice;
public class Ship {
private String name;
public Ship() {
this.name = "Ship Created";
}
public String getName() {
return this.name;
}
}
In mod10/B/module-info.java
module com.alice2 {
requires com.alice;
}
Main.java
package com.alice2;
import com.alice;
public class Main {
public static void main(String[] args) {
Ship x = new Ship();
System.out.println("Ship made" + x.getName());
}
}
Folder Tree:
mod10 (root)
|_ A
|_ com
|_ alice
|_ Ship.java
|_ module-info.java
|_ B
|_ com
|_ alice2
|_ Main.java
|_ module-info.java
I have a c++ library being linked with JNI to Java classes in an Android Studio project, and I am getting an error when trying to use env->GetFieldID() inside JNIAnalalog.cpp
This is the error I get when trying to load in a double from AnalogConfig.java
07-12 14:30:57.597 14360-14360/com.company.myapplication A/art: art/runtime/java_vm_ext.cc:470] JNI DETECTED ERROR IN APPLICATION: JNI NewGlobalRef called with pending exception java.lang.NoSuchFieldError: no "D" field "deadband" in class "[Lcom/automatak/dnp3/AnalogConfig;" or its superclasses
art/runtime/java_vm_ext.cc:470] at java.lang.String java.lang.Runtime.nativeLoad(java.lang.String, java.lang.ClassLoader, java.lang.String) (Runtime.java:-2)
art/runtime/java_vm_ext.cc:470] at java.lang.String java.lang.Runtime.doLoad(java.lang.String, java.lang.ClassLoader) (Runtime.java:1060)
art/runtime/java_vm_ext.cc:470] at void java.lang.Runtime.loadLibrary0(java.lang.ClassLoader, java.lang.String) (Runtime.java:975)
art/runtime/java_vm_ext.cc:470] at void java.lang.System.loadLibrary(java.lang.String) (System.java:1567)
art/runtime/java_vm_ext.cc:470] at void com.company.myapplication.MainActivity.<clinit>() (MainActivity.java:10) // This is where the c++ library is being loaded
I get the error no "D" field "deadband"; however, deadband is defined as a public double inside its java class, as seen below. So I am not sure why it is not considered a "D" field.
If I comment these two lines out and try to use the other env->GetFieldID(), I get a similar error: java.lang.NoSuchFieldError: no "[Lcom/automatak/dnp3/enums/EventAnalogVariation;" field "eventVariation" in class "[Lcom/automatak/dnp3/AnalogConfig;" or its superclasses.
I am not sure if the error is caused only by JNI or if Android Studio is part of the problem
This file set up inside my /app/src/main/ for the relevant files
.
|-- cpp
| `-- // Other libraries are located here
|
|-- dotnet
|
|-- java
| |-- com
| | |-- automatak
| | | `-- dnp3
| | | |-- AnalogConfig.java
| | | |-- EventConfig.java
| | | |-- enums
| | | | |-- EventAnalogVariation.java
| | | | `-- PointClass.java
| | | |-- impl
| | | `-- mock
| | `-- company
| | `-- myapplication
| | `-- MainActivity.java
| |-- cpp
| | |-- adapters
| | |-- JNI.cpp
| | `-- JNI.h
| | `-- jni
| | |-- JNIAnalogConfig.cpp
| | `-- JNIAnalogConfig.h
This is all of the relevant code inside the files
JNIAnalogConfig.cpp
#include "JNIAnalogConfig.h"
namespace jni
{
namespace cache
{
bool AnalogConfig::init(JNIEnv* env)
{
auto clazzTemp = env->FindClass("[Lcom/automatak/dnp3/AnalogConfig;");
this->clazz = (jclass) env->NewGlobalRef(clazzTemp);
env->DeleteLocalRef(clazzTemp);
this->deadbandField = env->GetFieldID(this->clazz, "deadband", "D");
if(!this->deadbandField) return false; // Crashes here
this->eventVariationField = env->GetFieldID(this->clazz, "eventVariation", "[Lcom/automatak/dnp3/enums/EventAnalogVariation;");
if(!this->eventVariationField) return false;
this->clazzField = env->GetFieldID(this->clazz, "clazz", "[Lcom/automatak/dnp3/enums/PointClass;");
if(!this->clazzField) return false;
return true;
}
}
}
JNIAnalog.Config.h
#ifndef OPENDNP3JAVA_JNIANALOGCONFIG_H
#define OPENDNP3JAVA_JNIANALOGCONFIG_H
#include <jni.h>
#include "../adapters/LocalRef.h"
namespace jni
{
struct JCache;
namespace cache
{
class AnalogConfig
{
friend struct jni::JCache;
bool init(JNIEnv* env);
void cleanup(JNIEnv* env);
public:
// field getter methods
jdouble getdeadband(JNIEnv* env, jobject instance);
LocalRef<jobject> geteventVariation(JNIEnv* env, jobject instance);
LocalRef<jobject> getstaticVariation(JNIEnv* env, jobject instance);
private:
jclass clazz = nullptr;
// field ids
jfieldID deadbandField = nullptr;
jfieldID eventVariationField = nullptr;
jfieldID clazzField = nullptr;
};
}
}
#endif
AnaglogConfig.java
import com.automatak.dnp3.enums.EventAnalogVariation;
public class AnalogConfig extends EventConfig {
public AnalogConfig(int index) {
super(index);
}
public double deadband = 0;
public EventAnalogVariation eventVariation = EventAnalogVariation.Group32Var1;
}
EventConfig.java
import com.automatak.dnp3.enums.PointClass;
public class EventConfig {
public EventConfig(int vIndex) {
this.vIndex = vIndex;
this.clazz = PointClass.Class1;
}
public int vIndex;
public PointClass clazz;
}
Any help is appreciated, thank you!
I have an java app which loads several plugins (.jar files) from FS dynamically. For each plugin I use such code:
URLClassLoader cl = URLClassLoader.newInstance( new URL[] { jarFile } );
Class<?> loadedClass = cl.loadClass( PluginEntry );
BasePlugin plugin = (basePlugin)loadedClass.newInstance();
And it's all ok. I have 2 plugins which inherit the same class, here is a structure:
interface BasePlugin -> app.jar
abstract class BaseA implements BasePlugin -> baseA.jar
class B extends BaseA -> pluginB.jar
class C extends BaseA -> pluginC.jar
But I need to put baseA package to both pluginB.jar and pluginC.jar and it's the problem. What I want to do is to remove BaseA.jar from pluginB/C packages and to load BaseA.jar dynamically from FS before loading pluginB and pluginC.
I tried to do this
URLClassLoader cl = URLClassLoader.newInstance( new URL[] { baseAjarFile } );
Class<?> loadedClass = cl.loadClass( PluginEntry );
but i cannot do anything with that loadedClass. And when App is loading pluginB/C it fails with java.lang.NoClassDefFoundError exception.
So how can I load this common abstract base class and make it available for pluginB/C?
Try
URLClassLoader.newInstance( new URL[] { baseAjarFile }, cl);