I'd like to implement my own custom ClassLoader. Basically it should do exactly what the default OSGi ClassLoader does.
I am using Karaf/Felix, so in my case that's org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader
The reason for implementing my own class loader is: I use JNI to load a DLL (with System.load()). But every time I redeploy my bundle I get a UnsatisfiedLinkError exception (Native Library xyz.dll already loaded in another classloader).
This is because the DLL is already loaded by the JVM - and stays as long as the ClassLoader that loaded it is NOT garbage collected.
So I want to do something like this:
CustomClassLoader cl = new CustomClassLoader();
Class ca = cl.findClass("myPackage.MyClass");
Object a = ca.newInstance();
Method p = ca.getMethod("myMethod");
p.invoke(a);
p = null;
ca = null;
a = null;
cl = null;
System.gc();
Hopefully after that the CustomClassLoader is garbage collected - and the DLL is unloaded from the container.
What I found strange: Even so each bundle has it's own ClassLoader, an osgi:uninstall <bundle> does not unload the DLL - which means that the bundles ClassLoader is still alive (and not garbage collected).
You also asked this question on the Apache Felix users list. My answer there was to leverage the native code mechanism that OSGi provides, as this solves your problems without you having to mess with classloaders. The mechanism provided by OSGi specifically addresses the case of updating a bundle with native code, so it should solve your problem nicely.
Related
I'm trying to build a dynamic database connection via Agroal inside a native image. It's not possible to use the default config params because I don't know the connection params at compile time. Is that even possible right now?
The connection is built like this at runtime:
AgroalDataSource.from(
AgroalDataSourceConfigurationSupplier()
...)
I'm currently seeing this error:
Class io.agroal.pool.ConnectionHandler[] is instantiated reflectively but was never registered.
Register the class by using org.graalvm.nativeimage.hosted.RuntimeReflection
The installed features include: [agroal, cdi, jdbc-h2, jdbc-mysql, jdbc-postgresql, kotlin, narayana-jta, resteasy, resteasy-jackson]
It runs fine on the JVM, but not using Graal. It feels like it should be possible and I'm probably missing something here. I was hoping adding agraol extension would be sufficient but obviously isn't picked up correctly.
The current situation is that we configure Agroal for native images only if you have a datasource defined using Quarkus configuration.
Thus for your use case, for now, you will have to do what we do automatically manually. What we do being registering some classes for reflection and including some resources in the native image.
See https://github.com/quarkusio/quarkus/blob/master/extensions/agroal/deployment/src/main/java/io/quarkus/agroal/deployment/AgroalProcessor.java#L91 and https://quarkus.io/guides/writing-native-applications-tips#using-a-configuration-file.
Obviously, that's not ideal. Could you open an issue on our GitHub so that we can discuss it internally and see if we should/can improve the situation?
In the end, you would still need some reflection registration for your JDBC driver though.
I'm trying to run a full ADTF configuration from my own C++ command-line application using the ADTF SDK. ADTF version: 2.9.1 (pretty old).
Here's what I have (want) to do:
Load manifest file
Load globals-xml
Load config-xml
2 & 3 are done, using the session-manager service - see ISessionManager interface: https://support.digitalwerk.net/adtf/v2/adtf_sdk_html_docs/classadtf_1_1_i_session_manager.html , functions LoadGlobalsFromFile & LoadConfigFromFile.
The problem is that I don't know how to do point 1: currently, instead of loading a manifest, I manually load the list of services myself using _runtime->RegisterPlugin, _runtime->CreateInstance and _runtime->RegisterObject.
What I've managed to do is to load only the namespace service and use the INamespace interface which has a method for loading manifest files: https://support.digitalwerk.net/adtf/v2/adtf_sdk_html_docs/classadtf_1_1_i_namespace.html - see ImportFile with ui32ImportFlags = CF_IMPORT_MANIFEST.
But this only loads the manifest settings into the namespace, it doesn't actually instantiate the services. I could do it manually, by:
Do _runtime->RegisterPlugin for every url under
root/plugins/ in the namespace
Do _runtime->CreateInstance for every objectid under
root/services/ in the namespace
But I want this to be more robust and I'm hoping there's already a service that handles the populated namespace subsequently and does these actions. Is there such a service?
Note: if you know how this could be done in ADTF3 that might also be of help for me, so don't hesitate to answer/comment
UPDATE
See "Flow of the system" on this page: https://support.digitalwerk.net/adtf/v2/adtf_sdk_html_docs/page_service_layer.html
Apparently the runtime instance itself handles the manifest file (see run-levels shutdown & kernel) but I don't know how I'm supposed to tell it where it is.
I've tried setting the command-line arguments to be count = 2 and the 2nd = manifest file path when instantiating cRuntime. It doesn't work :).
In ADTF3 you can just use the supplied cADTFSystem class to initiate an ADTF system and then use the ISessionManager interface to load a session of your choice.
Found the answer, not exactly what I expected though. I tried debugging adtf_runtime.exe to find out what arguments it passes to cRuntime.
The result is indeed similar to what I've suspected (and actually tried):
arg1 = adtf_runtime.exe (argv[0] in adtf_runtime)
arg2 = full path to manifest file (e.g. $(ADTF_DIR)\bin\adtf_devenv.manifest)
arg3 = basename of manifest file, without extension (e.g. "adtf_devenv")
While this suggested that cRuntime indeed is responsible with loading and handling the manifest, it turned out to be NOT quite so, passing the same arguments to it did not do the job. The answer came when I noticed that adtf_runtime.exe was actually using an extension of cRuntime called cRuntimeEx which is NOT part of the SDK (at least I haven't found it).
This class IS among the exported symbols of the ADTF SDK library, i.e. a "dumpbin /symbols adtfsdk_290.lib" renders at some point:
public: __cdecl adtf::cRuntimeEx::cRuntimeEx(int,char const * *
const,class ucom::IException * *)
but it is NOT part of the SDK (you won't find a header file defining it).
Among its methods you'll also find this:
protected: long __cdecl adtf::cRuntimeEx::LoadManifest(class adtf_util::cString const &,class std::set,class std::allocator > *,class ucom::IException * *)
Voila. And thus, unfortunately, I cannot achieve what I wanted in a robust fashion. :)
I ended up manually implementing the manifest-loading logic, since cRuntimeEx is not made available within the SDK. Something along these lines:
Use a cDOM instance to load the manifest file
Call FindNodes("/adtf:manifest/environment/variable") to find the environment-variables that need to be set and set them using "cSystem::SetEnvVariable"
Call FindNodes("/adtf:manifest/dependencies/platform") to find library dependencies and use cDynamicLinkage::Load to load the libraries that target the current platform (win32/linux)
Call FindNodes("/adtf:manifest/plugins/plugin") to find the services to be loaded using _runtime->RegisterPlugin (you may also handle "optional" attribute)
Call FindNodes("/adtf:manifest/services/service") to find the services that need to be created using _runtime->CreateInstance and _runtime->RegisterObject (you may also handle "optional" attribute)
And, finally, call FindNodes("/adtf:manifest/manifests/manifest") to (recursively) load child-manifests (you may also handle "optional" attribute)
The only thing you need to do is start the adtf launcher with the meta files (manifest. This works for adtf 2 as well as for adtf 3. It can be done (console) application. If you also want to do a little bit more in adtf 3, you can use adtf control instead of adtf launcher with its scripting interface (see the scripts under examples)
I am creating a managed object inside App.java (which is the main class of my module). I am using guice library with dropwizard framework and getting this exception only when running with IntelliJ if I run the same code with mvn it works perfectly fine which is weird and beyond my theory. So if someone has experienced something like this or have some theory behind this then please share. feel free to ask any detail.
environment.lifecycle().manage(new Managed() {
#Override
public void start() throws Exception {
}
#Override
public void stop() throws Exception {
}
});
Exception stacktrace:-
Exception in thread "main" com.google.inject.CreationException: Unable to create injector, see the following errors:
1) Injecting into inner classes is not supported. Please use a 'static' class (top-level or nested) instead of com.phonepe.growth.App$4.
at ru.vyarus.dropwizard.guice.module.installer.InstallerModule.bindExtension(InstallerModule.java:191) (via modules: ru.vyarus.dropwizard.guice.module.GuiceSupportModule -> ru.vyarus.dropwizard.guice.module.installer.InstallerModule)
1 error
at com.google.inject.internal.Errors.throwCreationExceptionIfErrorsExist(Errors.java:470)
at com.google.inject.internal.InternalInjectorCreator.initializeStatically(InternalInjectorCreator.java:155)
at com.google.inject.internal.InternalInjectorCreator.build(InternalInjectorCreator.java:107)
at com.google.inject.Guice.createInjector(Guice.java:99)
at ru.vyarus.dropwizard.guice.injector.DefaultInjectorFactory.createInjector(DefaultInjectorFactory.java:20)
at ru.vyarus.dropwizard.guice.GuiceBundle.createInjector(GuiceBundle.java:191)
at ru.vyarus.dropwizard.guice.GuiceBundle.run(GuiceBundle.java:138)
at ru.vyarus.dropwizard.guice.GuiceBundle.run(GuiceBundle.java:93)
at io.dropwizard.setup.Bootstrap.run(Bootstrap.java:200)
at io.dropwizard.cli.EnvironmentCommand.run(EnvironmentCommand.java:42)
at io.dropwizard.cli.ConfiguredCommand.run(ConfiguredCommand.java:85)
at io.dropwizard.cli.Cli.run(Cli.java:75)
at io.dropwizard.Application.run(Application.java:79)
at com.phonepe.growth.App.main(App.java:48)
This is actually a bug in guicey classpath scan (.enableAutoConfig).
Your application class is covered by a scan and so inner classes are also visible: when you start app from idea "Managed" inner class (created by the compiler for new Managed() {...}) is detected and registered as an extension which also means registration in guice context. But guice can't instantiate the inner class and throws an error.
You can enable extra diagnostic messages with .printLifecyclePhasesDetailed() (on guice bundle) and see that additional extension is indeed appear when running from idea.
Most certainly, when you run app from maven it builds into jar first and then launched. In this case, classpath scan works a bit differently and doesn't see inner classes (inside jar).. so everything works.
Please note that you don't need to instantiate and register managed object (and other common objects) manually: you can simply declare managed as a separate class and guicey will find it and properly register (both in guice and dropwizard). This is the expected way of extensions registrations, especially together with classpath scan.
I've been trying to implement following scenario:
Application [C++] uses CoCreateInstance on registered COM class (CReg/Reg.dll)
CReg class uses CoCreateInstance on regfree/SxS COM class (CFree/Free.dll).
CoCreateInstance returns REGDB_E_CLASSNOTREG.
Problem doesn't seem to be with manifests, because if I try to instantiate CFree directly via application, object is created with no problem.
I have checked and scenario above triggers Activation Context of CReg (I have checked with sxstrace) and even manifest of the CFree is loaded successfully (!) which should effect in correct regfree COM. If I change manifest of the CFree then Activation Contexts fails (which I believe is proof for me that it was correctly triggered and loaded before the change).
Is scenario with using registered COM [CReg] not possible to access CFree object? If it is possible, are there some special work in order to load it properly?
EDIT
With Joe's help, we worked out that the problem is where Free.dll is located.
Main application is (for example) in C:\Proj\App, both Reg.dll and Free.dll are in C:\Proj\Libs. Is there possibility to load regfree Free.dll which is in different location than application? Problem is that I can't place it in application directory or in application child directory (it has to be in external location).
I have tried to use ISOLATION_AWARE_ENABLED preprocessor definition on Reg.dll project, to trigger Activation context from Reg.dll directory. Manifest from Free.dll is loaded properly (sxstrace logs that) but CoCreateInstance call is still returning REGDB_E_CLASSNOTREG. This blog article points that it may be possible with this definition (but is not giving definite answer on this matter).
Anyone can help me solve this problem or at least point to the documentation that may give me an answer, whether it is possible or not, to load regfree dll from an external location?
I have been trying to refactor our Activiti implementation into using CDI but ran into a number of problems. I've spent way too much time trying to resolve this already, but I just can't let it go...I think I've pinned the problem down now, setting up a clean structured war without involving Activiti and have been able to reproduce what I think is the main problem.
Basically I have jar1 and jar2, both CDI enabled by including META-INF/beans.xml. Both jars specify a class in META-INF/services/test.TheTest pointing to implementations local to respective jar. jar1 depends on jar2. Also, both jars point to an implementation of javax.enterprise.inject.spi.Extension, triggering the scenario. In each implementation of Extension, I have a method like:
public void afterDeploymentValidation(
#Observes AfterDeploymentValidation event, BeanManager beanManager) {
System.out.println("In jar1 extension");
ServiceLoader<TheTest> loader = ServiceLoader.load(TheTest.class);
Iterator<TheTest> serviceIterator = loader.iterator();
List<TheTest> discoveredLookups = new ArrayList<TheTest>();
while (serviceIterator.hasNext()) {
TheTest serviceInstance = (TheTest) serviceIterator.next();
discoveredLookups.add(serviceInstance);
System.out.println(serviceInstance.getClass().getName());
}
}
Now, my problem is that the ServiceLoader does not see any implementations in either case when running WebLogic12c. The same code works perfectly fine in both Jboss 7.1.1 and Glassfish , listing both implementations of the test.TheTest interface.
Is it fair to assume that this is indeed a problem in WebLogic 12c or am I doing something wrong? Please bare in mind that I am simply trying to emulate the production setup we use when incorporating Activiti.
Regards,
/Petter
There is a Classloader Analysis Tool provided with WLS, have you seen if this will help with the diagnosis of your issue.
You can access this tool by going to ip:port/wls-cat/index.jsp
Where port will be the port of the managed server where your application is deployed.