Drools KieContainer from different ClassLoader - java

We have a Java application with different modules being deployed on Weblogic. We are using drools on different modules and tried to make the class that initializes the KieContainer a singleton by defining it as an enum class.
However, it seems that when we are in the production environment (where the application is deployed through a ear file) there are different ClassLoaders initializing this class and we get the following exception:
null java.lang.IllegalStateException: There's already another KieContainer created from a different ClassLoader;
at org.drools.compiler.kie.builder.impl.KieServicesImpl.getKieClasspathContainer(KieServicesImpl.java:88);
at org.drools.compiler.kie.builder.impl.KieServicesImpl.getKieClasspathContainer(KieServicesImpl.java:73);
Do you have any suggestion on how to solve this?

We had the same problem though in a different environment (Kafka, Weld SE). Though counter-intuitive, invoking
// Answer the cuurent container if it exists else create a new container
KieServices.Factory.get().getKieClasspathContainer();
not
// Always create a new container
KieServices.Factory.get().newKieClasspathContainer();
fixed most things for us.
Also, before the container goes out of scope be sure to invoke:
KieServices.Factory.get().getKieClasspathContainer().dispose();
This will release the container and its resources from the Drools global singleton.
We also had problems running unit tests in Maven as the Surefire plugin by default does not recreate a JVM per test while Drools assumes that an instance of its global singleton will be created just once per JVM invocation. This is resolved by having Surefire recreate a clean JVM environment per test. Adjust your
pom.xml
by adding
<reuseForks>false</reuseForks>
to your Surefire configuration. For example:
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<executions>
<execution>
<id>default-test</id>
<configuration>
<reuseForks>false</reuseForks>
</configuration>
</execution>
</executions>
</plugin>
Also, you might consider assigning each Java EE module its own KieContainer
KieContainer getKieClasspathContainer(String containerId);
This would allow the lifecycle of each Java EE module to be synchronised to that of each Drools container module.

The drools code checks if your specified class loader and the current instance this.getClass().getClassLoader() is the same, if not errors out with the KieContainer already exists eror . If you dont specify a classloader it uses Thread.currentThread().getContextClassLoader() , which is different from this.getClass().getClassLoader() in some situvations. Simple solution is to use
KieServices.Factory.get().getKieClasspathContainer(this.getClass().getClassLoader())

Related

OpenTelemetry Muzzle matcher warn; instrumentation skipped in OSGi container

For a project I'm doing I'm trying to run OpenTelemetry (OTEL) in an OSGi-container. Hereby the situation:
I have a simple Maven/Java-application in which includes the #WithSpan-annotations via the io.opentelemetry.opentelemetry-extension-annotations dependency, version 1.12.0 (can ofcourse be changed). The application for now is fairly simple; it just calls different methods who are annotated and do some logging.
#WithSpan("multiple")
private static int multiple(int number) {
return number * 2;
}
The java-agent is successfully attached to the container via the start-up script
The collector pipeline is running successfully via a YAML-file, this has been verified by running the jar of another project with the agent against it. This collects the results and displays it in Jaeger.
However, due to the limited exports of the dependency, the main application was not able to run, because the dependency did not export its classes/methods/etc. Therefore I wrapped this as an OSGi-bundle. I did this as following:
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<configuration>
<instructions>
<Bundle-Vendor>opentelemetry (Repackaged)</Bundle-Vendor>
<Embed-Dependency>
groupId=io.opentelemetry;artifactId=opentelemetry-extension-annotations;type=jar;classifier=;inline=true
</Embed-Dependency>
<Embed-Transitive>false</Embed-Transitive>
<Export-Package>*</Export-Package>
<Import-Package>!*</Import-Package>
<Private-Package>io.opentelemetry.*</Private-Package>
</instructions>
</configuration>
</plugin>
However when now having installed the wrapped OTEL-bundle in the container to which the agent is attached, and running the test project, I get the following error:
[otel.javaagent 2022-XX-XX XX:XX:XX:XXX +0000] [shell remote=/127.0.0.1:52855] WARN muzzleMatcher - Instrumentation skipped, mismatched references were found: opentelemetry-annotations [class io.opentelemetry.javaagent.instrumentation.otelannotations.WithSpanInstrumentationModule] on org.eclipse.osgi.internal.loader.EquinoxClassLoader#c89a9f[com.example.test-project:1.0.0.SNAPSHOT(id=15)]
And logically no spans show up in Jaeger. Does anybody have any suggestion? I tried to change the versions, include transitive dependencies, etcetera. But nothing seems to work.

No such method : JRE picks wrong class if ambiguous occurs

I my application I am facing below exception,
/component/ProviderServices;Lcom/sun/jersey/core/spi/factory/InjectableProviderFactory;)V
at com.sun.jersey.api.client.Client.<init>(Client.java:212)
at com.sun.jersey.api.client.Client.<init>(Client.java:150)
at com.sun.jersey.api.client.Client.create(Client.java:476)
at com.example.data.DataReader.getData(DataReader.java:25)
at com.example.data.TestServlet.doGet(TestServlet.java:41)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:620)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
I found the reason for this exception but I don't know how to resolve it. The problem is I am having two jars namely jersey-bundle-1.1.5.1 and jersey-core-1.17.1 in my classpath. ContextResolverFactory.java is present in both jars with same package name. init method is present in jersey-core-1.17.1 but not in jersey-bundle-1.1.5.1. In windows build environment it is working fine. That means the JRE picks the ContextResolverFactory.java of jersey-core-1.17.1 correctly and executes the init method. Whereas in linux environment the JRE picks ContextResolverFactory.java of jersey-bundle-1.1.5.1 and tries to invoke the init method and throwing the above exception. I cant remove a jar blindly, since both jars are needed for different business purpose.
How to fix it in both linux and windows environment?
Why it is working fine in windows environment but not in linux environment?
I fully agree with the commenters. Per se it is bad practice to have the same class (in the same package) on the classpath multiple times. This will almost always cause troubles. The best thing would be to check whether or not you can make your code work with jersey 1.17.1 and use only the jersey-core-1.17.1 jar.
However, I also understand that there are situations where you do not have control over these dependencies i.e. where 3rd party libraries depend on specific versions of a certain library and you just have to work around these issues.
In these cases it is important to notice that the default java classloaders respect the order of the elements in the classpath. I assume that the order of the CLASSPATH variable in your Linux installation is different from that on your Windows installation.
If you are using an IDE such as Eclipse during your development please check the build path setup there and try setting the CLASSPATH variable on your production in exactly the same order.
For your reference please also check these other questions on stackoverflow:
Controlling the order of how JARs are loaded in the classpath
Is the order of the value inside the CLASSPATH matter?
In the case of Tomcat the order of the JAR files in WEB-INF/lib cannot be defined. The only thing you could do here would be to ship the JAR file that needs to be loaded first to some other directory in your production environment such as the JRE/lib directory, the Tomcat/common directory or the Tomcat/shared directory. Which all have priority over the WEB-INF/lib directory. See Control the classpath ordering of jars in WEB-INF/lib on Tomcat 5? for details on how this worked on older Tomcat versions.
One of the guiding principles that I try to follow when I develop my own applications is that I want to make them "dummy-proof." I want to make it as easy as possible on the end user.
Therefore, I would change the build of the applications to include ContextResolverFactory.class in your final jar (from jersey-core-1.17.1.jar). That's the general approach. The specific tool you use to achieve this might vary.
I would use maven and the maven-shade-plugin. This plugin can even do what's called a relocation where you provide the original package in the pattern tag, and you provide the desire new package location in the shadedPattern tag:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>1.6</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<relocations>
<relocation>
<pattern>com.sun.jersey.core.spi.factory</pattern>
<shadedPattern>${project.groupId}.${project.artifactId}.com.sun.jersey.core.spi.factory</shadedPattern>
</relocation>
</relocations>
<artifactSet>
<includes>
<include>com.sun.jersey:jersey-core</include>
</includes>
</artifactSet>
<minimizeJar>true</minimizeJar>
</configuration>
</execution>
</executions>
</plugin>
</plugins
</build>
Even if you're not experienced with maven, you could still make a small side project whose only purpose is to refactor the package location. Then, you would add this dependency to your project and use it to reliably access the init() method.
If you are experienced with maven, then I highly recommend splitting your project up into what's called a maven multi module POM project. This would be the new build order:
The Interface module
The Implementation Layer
The Runtime module
Where the Implementation Layer typically consists of many different modules that all depend upon the Interface module. And the Runtime module chooses the correct implementation at runtime.
You might not see the value if you currently only have one implementation... But down the road, it adds flexibility if you need to add more implementations, because you will be able to add them easily. Because your code never directly references an implementation, but rather, it always uses the interface, and it doesn't care which implementation is used.
So, this would make it harder on you, the developer, but easier on the end-user. Whether they're on windows, linux, or mac, it just works!
After checking the source-code, I noticed that all the logic of init() was moved to the constructor.
So another option, is to simply use the new constructor and catch the exceptional circumstance where it's not there, in which case, you would just use the default constructor followed by the init() method:
ContextResolverFactory factory = null;
try {
factory = new ContextResolverFactory(providerServies, ipf);
} catch (InvalidClassException ex) {
factory = new ContextResolverFactory().init(providerServices, ipf);
}
// ...
ContextResolver<MyType> cr = factory.resolve(type, mediaType);
if (cr == null) // handle null and not null...
Hopefully this helps. Good luck!

Grizzly / Jersey resources inside an executable generated with launch4j

I am having issues running a Grizzly Web Server with Jersey Resources when the web server is run from an executable generated by launch4j. The resources are inside the executable generated by launch4j, but the program cannot access them. If I execute it from the jar directly it runs without problems. But I need to provide an .exe to run the webserver.
Webserver code:
String uri = buildURI();
HashMap<String,String> initParams = new HashMap<String,String>();
initParams.put(PackagesResourceConfig.PROPERTY_PACKAGES, "my.packages.resources");
initParams.put(WebAppResourceConfig.FEATURE_XMLROOTELEMENT_PROCESSING, "true");
initParams.put(FeaturesAndProperties.FEATURE_FORMATTED, "true");
HttpServer httpServer = GrizzlyWebContainerFactory.create(uri, initParams);
httpServer.start();
I have a class in my.packages.resources called InfoRestResource with the following code:
#Path("/info")
public class InfoRestResource {
#GET
#Path("/time")
#Produces(MediaType.TEXT_PLAIN)
public String getTime() {
return String.valueOf(new Date().getTime());
}
}
launchj4 Maven configuration:
<plugin>
<groupId>org.bluestemsoftware.open.maven.plugin</groupId>
<artifactId>launch4j-plugin</artifactId>
<version>1.5.0.0</version>
<executions>
<execution>
<id>launch4j</id>
<phase>package</phase>
<goals>
<goal>launch4j</goal>
</goals>
<configuration>
<dontWrapJar>false</dontWrapJar>
<headerType>console</headerType>
<jar>${basedir}/target/my-jar-with-dependencies.jar</jar>
<outfile>${basedir}/target/program.exe</outfile>
<errTitle></errTitle>
<cmdLine></cmdLine>
<chdir></chdir>
<priority>normal</priority>
<downloadUrl>http://java.com/download</downloadUrl>
<supportUrl></supportUrl>
<customProcName>true</customProcName>
<stayAlive>false</stayAlive>
<manifest></manifest>
<singleInstance>
<mutexName>MyLaunch4jMutex</mutexName>
<windowTitle></windowTitle>
</singleInstance>
<jre>
<path></path>
<minVersion>1.6.0</minVersion>
<maxVersion></maxVersion>
<initialHeapSize>64</initialHeapSize>
<maxHeapSize>512</maxHeapSize>
</jre>
</configuration>
</execution>
</executions>
</plugin>
Output
When I run the .exe:
WebServer started at http://localhost:9200/program/
Scanning for root resource and provider classes in the packages:
my.packages.resources
Initiating Jersey application, version 'Jersey: 1.6 03/25/2011 01:14 PM'
The ResourceConfig instance does not contain any root resource classes.
service exception:
com.sun.jersey.api.container.ContainerException: The ResourceConfig instance does not contain any root resource classes.
at com.sun.jersey.server.impl.application.RootResourceUriRules.<init>(RootResourceUriRules.java:103)
at com.sun.jersey.server.impl.application.WebApplicationImpl._initiate(WebApplicationImpl.java:1178)
at com.sun.jersey.server.impl.application.WebApplicationImpl.access$600(WebApplicationImpl.java:159)
at com.sun.jersey.server.impl.application.WebApplicationImpl$12.f(WebApplicationImpl.java:693)
at com.sun.jersey.server.impl.application.WebApplicationImpl$12.f(WebApplicationImpl.java:690)
at com.sun.jersey.spi.inject.Errors.processWithErrors(Errors.java:193)
at com.sun.jersey.server.impl.application.WebApplicationImpl.initiate(WebApplicationImpl.java:690)
at com.sun.jersey.server.impl.application.WebApplicationImpl.initiate(WebApplicationImpl.java:685)
at com.sun.jersey.spi.container.servlet.ServletContainer.initiate(ServletContainer.java:488)
at com.sun.jersey.spi.container.servlet.ServletContainer$InternalWebComponent.initiate(ServletContainer.java:318)
at com.sun.jersey.spi.container.servlet.WebComponent.load(WebComponent.java:601)
On the other hand, when I run it from the Jar, it works fine and I get the following output:
WebServer started at http://localhost:9200/program/
Scanning for root resource and provider classes in the packages:
my.packages.resources
Root resource classes found:
class my.packages.resources.InfoRestResource
I would really appreciate any help in solving this issue! Thank you!
We had two modules first - rest-api, where all the classes jersey supposed to find were, and restmod that was the exe setup along with other classes (Main class with Jetty bootstrap, etc.).
Our Launch4j treatment was unified in parent POM for all modules and it included JAR into EXE. After we merged rest-api with restmod (because it didn't make any sense for us anymore to keep them separated) we ran into this same problem. Devel/java worked just fine, but EXE version returned 404 for any endpoint. I debugged for a while and realized it seems to be Jersey's problem with EXE packaging. Found your questions and no answer.
What we did in the end: We introduced restmod-exe module. This time it did NOT contain any classes, it merely wrapped icons (two resources) into the JAR and included that into EXE as before (so no change to the prefered setup from our parent POM). There was also no change in how we managed dependencies (we put all of them to dependency_libs with maven-dependency-plugin). The only difference was that the main class was now in restmod.jar that was in that depenency_libs dir - but this causes no problem.
So we're back to two modules, but the second one's responsibility is EXE packaging and nothing else. Works for us.
BTW: The linked blogpost also deals with problem how to deploy WAR structure from EXE, which was our case before (using Jetty). Now it isn't - but it just demonstrates that Jersey is not the only one not willing to deal with EXE/ZIPs in classpath.

How can I pass Hudson's env.EXECUTOR_NUMBER to log4j properties and Java's System.getProperty

My Hudson job calls a single XML database collection and a single log file. E.g.
/db/project
${user.home}/logs/logging.log
I'd like to inject Hudson's env.EXECUTOR_NUMBER in both paths to avoid concurrent execution clashes. E.g.
/db/project {$EXECUTOR_NUMBER}
{$user.home}/logs {$EXECUTOR_NUMBER}/logging.log
I've found out the following:
Hudson site states that I need to pass Hudson's EXECUTOR_NUMBER to Maven as part of the build goal.
Log4J can only use system variables not environment variables (as these are a platform specific concept)
Now that I've got Hudson's EXECUTOR_NUMBER variable specified in the build goal, how can I use this as a system property for use by Log4J and Java's System.getProperties() class?
You can pass System Properties to any Java process using the -D syntax.
mvn clean install -DEXECUTOR_NUMBER={$EXECUTOR_NUMBER}
For a test class in a forked run, you will additionally have to configure the surefire plugin to pass the system property to the forked vm:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.9</version>
<configuration>
<systemPropertyVariables>
<EXECUTOR_NUMBER>${EXECUTOR_NUMBER}</EXECUTOR_NUMBER>
</systemPropertyVariables>
</configuration>
</plugin>

Using Maven to build separate JAR files for unit testing a custom class loader

As part of my current project I've created a custom class loader. Part of the unit tests for the custom loader involves using some JAR files to demonstrate the proper behavior of the loader.
I'd like to build the test JAR files from Java sources ahead of running the actual unit tests. Further, the test JAR files cannot be on the class path when the unit tests are run, since I want to dynamically load them during the test execution.
Is there a standard pattern for accomplishing this sort of "build some JARs on the side before the test phase but leave them out of the class path" requirement? I can't believe I'm the first person to try doing this with Maven 2, but I can't seem to hit on the right POM structure and dependencies. Usually I end up with some of the test jars not being built ahead of the test phase, but I've also had problems with inconsistent order-of-build causing the build to work properly on one machine, but fail to build some of the test jars on another.
The simplest thing to do is to set up another project to package the classes for your test jar, then set that as a normal test-scoped dependency.
If you don't want/aren't able to do that, you can use the assembly plugin to create a jar in the process-test-classes phase (i.e. after the tests have been compiled but before the tests are executed). The configuration below will invoke the assembly plugin to create a jar called classloader-test-deps in that phase in the target directory. Your tests can then use that jar as needed.
The assembly plugin uses an assembly descriptor (in src/main/assembly, called test-assembly.xml) that packages the contents of target/test-classes. I've set up a filter to include the contents of com.test package and its children. This assumes you have some package name convention you can apply for the contents of the jar.
The assembly plugin will by default attach the jar as an additional artifact, by specifying attach as false, it will not be installed/deployed.
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.2-beta-2</version>
<executions>
<execution>
<id>create-test-dependency</id>
<phase>process-test-classes</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<finalName>classloader-test-deps</finalName>
<attach>false</attach>
<descriptors>
<descriptor>src/main/assembly/test-assembly.xml</descriptor>
</descriptors>
</configuration>
</execution>
</executions>
</plugin>
This is the content of test-assembly.xml
<assembly>
<id>test-classloader</id>
<formats>
<format>jar</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<fileSets>
<fileSet>
<directory>${project.build.testOutputDirectory}</directory>
<outputDirectory>/</outputDirectory>
<!--modify/add include to match your package(s) -->
<includes>
<include>com/test/**</include>
</includes>
</fileSet>
</fileSets>
</assembly>
I would try to set up everything your tests needs from within the test. The main advantage is that there is no magic unseen setup that is implicit for the test. The test can run in every environment. Additionally it is much easier to add new strictly isolated scenarios as you are not dependent on some mixed scenario setup.
The setup should not be too hard:
serialize a java class:
with some type code engineering library
Alternatively, use a java class file renamed to some file suffix other than .class. Put it under the test resource folder and load with the class loader (getResourceAsStream(...)).
zip the class file (`java.util.zip.GZIPOutputStream`)
load the class file with your class loader
There is an alternative approach that uses the java class loader design and works without generation of additional classes.
Java has a class loader hierarchy. Every class loader has a parent class loader. The root of the class loader hierarchy is the boot class loader. When a class is loaded with a class loader it will try to load the class first with the parent class loader and then itself.
You can load the test class with the current class loader. Jar it and load it with your own class loader. The only difference is that you set the parent class loader to one that cannot load your test class.
String resource = My.class.getName().replace(".", "/") + ".class";
//class loader of your test class
ClassLoader myClassLoader = currentThread().getContextClassLoader();
assert ! toList(myClassLoader.getResources(resource)).isEmpty();
//just to be sure that the resource cannot be loaded from the parent classloader
ClassLoader parentClassloader = getSystemClassLoader().getParent();
assert toList(parentClassloader.getResources(resource)).isEmpty();
//your class loader
URLClassLoader myLoader = new URLClassLoader(new URL[0], parentClassloader);
assert toList(myLoader.getResources(resource)).isEmpty();
Maven resolves build order via dependency analysis, so normally your JARs would build in order because the one that uses your test JARs would simply declare them as dependencies. However, dependencies are also placed on the classpath. The "scope" of a dependency determines which classpath it goes on. For example 'compile' dependencies are on the classpath for compiling, testing, and running; 'runtime' dependencies are on the classpath for testing and running; 'test' dependencies are only on the classpath during test. Unfortunately, you have a case not covered by any of the available scopes: you have a dependency, but you don't want it on the classpath. This is a fringe use case and is why you are having trouble discovering examples.
So, unless some Maven guru rears up to indicate the contrary, I suggest this is impossible without writing a special Maven plugin. Instead of that, however, I recommend something else. Do you really need custom-built JARs to test your classloader? That sounds fishy to me. Perhaps you can use any old JAR? If so, I would use the maven-dependency-plugin to copy some JAR known to always be in your repository (log4j for example) into your local module's target directory. Your test can then access that JAR via filepath at target/log4j-xxx.jar and you can do your thing.

Categories

Resources