Classloader Issues with Envers in OSGi - osgi

I've successfully started Hibernate in an OSGi context, and now I want to add Envers.
The documentation claims that's possible. I don't believe it anymore. There is no documentation of any kind for this subject and nobody seems to have actually done it. Moreover even with a Blueprint implementation I had to hack the classloader to make Hibernate even find Envers:
osgiClassLoader = new org.hibernate.osgi.OsgiClassLoader();
osgiClassLoader.addBundle(requestingBundle);
osgiClassLoader.addBundle(FrameworkUtil.getBundle(SessionFactory.class));
osgiClassLoader.addBundle(FrameworkUtil.getBundle(HibernateEntityManagerFactory.class));
osgiClassLoader.addBundle(FrameworkUtil.getBundle(EnversService.class));
Thread.currentThread().setContextClassLoader(osgiClassLoader);
(I feel like I should be asking: "Is Envers in OSGi even possible?" So if you have a definite answer for that question, please let me know. I've been spending way too much time on these issues.)
However the actual problem has nothing to do with Hibernate / Envers and all with OSGi. Both want to access the entities and enums used. With reflection nonetheless. Of course they can't. And of course I can't add Import-Packages.
The relevant stack trace looks something like this:
Caused by: java.lang.ClassNotFoundException: org.acme.project.MyEnum cannot be found by org.hibernate.core_5.1.0.Final
at org.eclipse.osgi.internal.loader.BundleLoader.findClassInternal(BundleLoader.java:461)
at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:372)
at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:364)
at org.eclipse.osgi.internal.loader.ModuleClassLoader.loadClass(ModuleClassLoader.java:161)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:348)
at org.hibernate.internal.util.ReflectHelper.classForName(ReflectHelper.java:151)
at org.hibernate.type.EnumType.setParameterValues(EnumType.java:105)
... 62 more
Normally with OSGi-agnostic frameworks I'd just add something like this to the MANIFEST.MF:
// to org.hibernate.core
Eclipse-BuddyPolicy: registered
// to org.acme.project
Eclipse-RegisterBuddy: org.hibernate.core
However I can't add anything to Hibernate's manifest. I tried contributing the line via fragment, but that did not work either.
I even tried adding the entire Hibernate dependencies as JAR into a plug-in to add the above. It won't work.
How to resolve these classloader issues?

If your #Audited class references an Enum, org.hibernate.core needs to import the classes package. The best way to do this is to register a fragment bundle.
Also, it is wise to make sure your start level for hibernate bundles is lower than the start levels for entity bundles.
Here is a snippet from a sample Gradle script to create a fragment bundle. Make sure your change the org.hibernate.core bundle version to the one you are using, and also update the 'your.package' to the one that contains your Enum.
/*
When using Envers, entities that reference Enum classes must be imported (Import-Package) by the org.hibernate.core bundle.
*/
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'biz.aQute.bnd:biz.aQute.bnd.gradle:3.2.0'
}
}
apply plugin: 'biz.aQute.bnd.builder'
jar {
manifest {
attributes(
'Fragment-Host': 'org.hibernate.core;' + 'bundle-version=' + '5.2.9.Final',
'Import-Package': 'your.package'
)
}
}

Late to the party, but throwing this here in case anyone else needs it:
Hibernate bootstrapping looks fairly different in OSGi than it does in SE/EE land. We have numerous tickets in JIRA tracking enhancements, especially to make things more dynamic and to reduce the brittleness of startup ordering (Steven is absolutely correct that hibernate-core, hibernate-envers, etc. must currently be started first prior to your bundle).
I'd highly advise against the bundle fragment approaches, but I'll comment why on Steven's answer.
Our hibernate-demos project has a few OSGi quickstarts, all of which include Envers setup. This one is probably more of what you're looking for: https://github.com/hibernate/hibernate-demos/tree/master/hibernate-orm/osgi/unmanaged-native

Related

How to automatically register all available interface implementations in Quarkus?

I'm trying to adapt a library to be usable in Quarkus native mode. Since it's reflection-heavy, I need to manually register all implementations of certain interfaces.
What I've done so far and which seems to work fine for user code:
private static void registerAllImplementations(CombinedIndexBuildItem combinedIndexBuildItem,
BuildProducer<ReflectiveHierarchyBuildItem> reflectiveHierarchyClass,
Class<?>... classNames) {
for (Class<?> klass : classNames) {
combinedIndexBuildItem.getIndex().getAllKnownImplementors(DotName.createSimple(klass.getName())).stream()
.map(ci -> new ReflectiveHierarchyBuildItem(Type.create(ci.name(), Type.Kind.CLASS)))
.forEach(reflectiveHierarchyClass::produce);
}
}
However, the below line doesn't pick up implementors that come from external jars:
combinedIndexBuildItem.getIndex().getAllKnownImplementors(...)
It's not a tragedy, but it'd be much more future-proof if one did not need to pay attention to the internals of some external jar and make sure that all relevant implementations get registered manually.
Do you have any clues?
Behind the scene, Quarkus uses Jandex to index your sources. This is Jandex that provides the CombinedIndexBuildItem so you need all the external jar to be indexed by Jandex.
For this you can add the Jandex maven plugin to those external JAR or add some configuration options for each jar :
quarkus.index-dependency.<name>.group-id=
quarkus.index-dependency.<name>.artifact-id=
More information here: https://quarkus.io/guides/cdi-reference#how-to-generate-a-jandex-index

How to statically weave JPA entities using EclipseLink when there is no persistence.xml as the entities are managed by Spring

I've got a project that is Spring based, so the entity manager is set up progammatically, with no need for persistence.xml files to list all the entities.
I'm currently using load time weaving but am trying to get static weaving working using Eclipselink and Gradle. I want to replicate what is performed by the maven eclipselink plugin:
https://github.com/ethlo/eclipselink-maven-plugin
I have the following gradle set up (note that it's Kotlin DSL not groovy):
task<JavaExec>("performJPAWeaving") {
val compileJava: JavaCompile = tasks.getByName("compileJava") as JavaCompile
dependsOn(compileJava)
val destinationDir = compileJava.destinationDir
println("Statically weaving classes in $destinationDir")
inputs.dir(destinationDir)
outputs.dir(destinationDir)
main = "org.eclipse.persistence.tools.weaving.jpa.StaticWeave"
args = listOf("-persistenceinfo", "src/main/resources", destinationDir.getAbsolutePath(), destinationDir.getAbsolutePath())
classpath = configurations.getByName("compile")
}
When I try and run the task the weaving tasks fails as it's looking for a non-existent persistence.xml.
Is there any way you can statically weave JPA entities in a Spring based JPA project ?
Exception Description: An exception was thrown while processing persistence.xml from URL: file:/home/blabla/trunk/my-module/src/main/resources/
Internal Exception: java.net.MalformedURLException
at org.eclipse.persistence.exceptions.PersistenceUnitLoadingException.exceptionProcessingPersistenceXML(PersistenceUnitLoadingException.java:117)
at org.eclipse.persistence.internal.jpa.deployment.PersistenceUnitProcessor.processPersistenceXML(PersistenceUnitProcessor.java:579)
at org.eclipse.persistence.internal.jpa.deployment.PersistenceUnitProcessor.processPersistenceArchive(PersistenceUnitProcessor.java:536)
... 6 more
Caused by: java.net.MalformedURLException
at java.net.URL.<init>(URL.java:627)
at java.net.URL.<init>(URL.java:490)
at java.net.URL.<init>(URL.java:439)
at com.sun.org.apache.xerces.internal.impl.XMLEntityManager.setupCurrentEntity(XMLEntityManager.java:620)
at com.sun.org.apache.xerces.internal.impl.XMLVersionDetector.determineDocVersion(XMLVersionDetector.java:148)
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:806)
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:771)
at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:141)
at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1213)
at com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(SAXParserImpl.java:643)
at org.eclipse.persistence.internal.jpa.deployment.PersistenceUnitProcessor.processPersistenceXML(PersistenceUnitProcessor.java:577)
... 7 more
Caused by: java.lang.NullPointerException
at java.net.URL.<init>(URL.java:532)
... 17 more
According to org.eclipse.persistence.tools.weaving.jpa.StaticWeave documentation, it requires the persistence.xml in place to generate the static weaving sources.
Usage:
StaticWeave [options] source target
Options:
-classpath
Set the user class path, use ";" as the delimiter in Window system and
":" in Unix system.
-log
The path of log file, the standard output will be the default.
-loglevel
Specify a literal value for eclipselink log level(OFF,SEVERE,WARNING,INFO,CONFIG,FINE,FINER,FINEST). The default
value is OFF.
-persistenceinfo
The path contains META-INF/persistence.xml. This is ONLY required when the source does not include it. The classpath must contain all
the classes necessary in order to perform weaving.
I run a maven build using eclipselink maven plugin, it works without the persistence.xml as you mentioned, because it generates the persistence.xml
before invoking the StaticWeave.class when It is not located in the CLASSPATH, using this method.
private void processPersistenceXml(ClassLoader classLoader, Set<String> entityClasses)
{
final File targetFile = new File(this.persistenceInfoLocation + "/META-INF/persistence.xml");
getLog().info("persistence.xml location: " + targetFile);
final String name = project.getArtifactId();
final Document doc = targetFile.exists() ? PersistenceXmlHelper.parseXml(targetFile) : PersistenceXmlHelper.createXml(name);
checkExisting(targetFile, classLoader, doc, entityClasses);
PersistenceXmlHelper.appendClasses(doc, entityClasses);
PersistenceXmlHelper.outputXml(doc, targetFile);
}
The complete source code is here
I believe you could follow the same approach in your gradle build.
Kinda late to the party but this is definitely possible with Gradle.
There are 3 steps to do in order to make this work:
Copy the persistence.xml file into the source folder next to the classes
Do the weaving
Remove the persistence.xml file from the classes source folder to avoid duplicate persistence.xml conflicts on the classpath
Also, it's very important to hook the weaving process into the compileJava task's last step in order to not break Gradle's up-to-date check, otherwise Gradle will just recompile everything all the time which can be quite inconvenient when developing.
For a more detailed explanation, check out my article on it: EclipseLink static weaving with Gradle.
I admit, I do not completely understand what you mean by weaving. My answer might help if you need to create dynamically PersistenceUnits which provide JPA-Entitymanagers, and if these units should be able to create a Db-Schema (for example in H2) and manage Entities based dynamically on the classes you provide at runtime.
The code-example I am mentioning later, does not work with JPA in Spring but in Weld. I think the answer to your question is related to how EntityManagers are created and what classes the PersistenceUnit, which creates the EntityManager, does manage. There is no difference between those two. Instead of using the EntityManagerFactory as CDI-Producer you might Autowire it or register it using an old fashioned application-context. Therefore I think the answer to your question lies in the following official sources:
PersistenceProviderResolverHolder and
PersistenceProvider#createEntityManagerFactory(getPersistenceUnitName(), properties)
properties is the replacement for the persistence.xml, where a SEPersistenceUnitInfo-Object can be registered in.
To start look at: PersistenceProviderResolverHolder
Later: PersistenceProvider
or you can try to understand how my code (see below) is doing that. But I have to admit, I am not very proud of this part of that software, sorry.
Those classes and objects are used by me to create a module that enables the simulation of a server deployed JPA-WAR-File.
To do that, it scans some classes and identifies Entities.
Later in the Testcode a so called PersistenceFactory creates EntityManager and Datasources. If eclipselink is used this factory weaves those classes together. You need no persistence.xml. The working there might be help to answer your question.
If you look at:
ioc-unit-ejb:TestPersistencefactory
search for the creation of SEPersistenceUnitInfo. That Interface got fed by a list of classes which it returns as
#Override
public List<String> getManagedClassNames() {
return TestPersistenceFactory.this.getManagedClassNames();
}
This object is used to create a Persistencefactory with the help of a PersistenceProvider. This can be discovered as soon as eclipselink is available in the classpath.
The code is not easy to be understood because it allows both Hibernate or Eclipselink to be used for JPA, that depends on the availability of the jars in the classpath.

NullPointerException com.liferay.portal.spring.util.SpringFactoryImpl.setBeanDefinitions

I'm using springFramework and I try to setBeanDefinitions, the problem is that this methode need a Map beanDefinition as a param... could U tell me plz how I could instantiate this param?
NullPointerException at com.liferay.portal.spring.util.SpringFactoryImpl.setBeanDefinitions(SpringFactoryImpl.java:56)
additional information:
I try to deploy a liferay project without using liferay configuration files (only springFramework libraries), I created my own sessionFactory, my own dataSource ... etc!!
when I run the program, I'm able to create dataBase Schema basing on portlet-hbm.xml information... well now I try to instantiate beans for portal-spring.xml.. (which are xxxxpersistance.java)! those latters told me that they use 'com.liferay.portal.kernel.dao.orm.SessionFactory' as a required type and it can not convert property value of type 'org.hibernate.impl.SessionFactoryImpl'!! so I tried to use the liferay libraries only for those beans and I try to instanciate them manually... but I wasn't able to setBeanDefinitions cause I need a Map beanDefinition as a param... I don't know if there is a way to get them using sessionFactory or not!!
Thanks again
You only mention junit in the tags to your question. I'd recommend to write unit tests without relying on the whole Liferay infrastructure. That will tremendously lower your required setup efforts and simplify your life a lot.

XSLT ClassCastException in WebSphere when Spring tries to create an AnnotationMethodHandlerAdapter

When starting WebSphere, I get this exception:
Could not instantiate bean class [org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter]:
Constructor threw exception; nested exception is java.lang.ClassCastException:
com.ibm.xtq.xslt.jaxp.compiler.TransformerFactoryImpl incompatible with
javax.xml.transform.TransformerFactory
Caused by: java.lang.ClassCastException: com.ibm.xtq.xslt.jaxp.compiler.TransformerFactoryImpl
incompatible with javax.xml.transform.TransformerFactory
at javax.xml.transform.TransformerFactory.newInstance(Unknown Source)
at org.springframework.http.converter.xml.AbstractXmlHttpMessageConverter.<init>(AbstractXmlHttpMessageConverter.java:47)
at org.springframework.http.converter.xml.SourceHttpMessageConverter.<init>(SourceHttpMessageConverter.java:45)
at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.<init>(AnnotationMethodHandlerAdapter.java:197)
This doesn't seem have any impact on any beans in my applicationContext.xml but it's still odd. For me, this looks as if IBM classes are leaking into my application.
How can I fix this? I already set the option "Access to internal server classes" to "Restrict".
It was indeed a class-loading issue, however this cannot be solved by changing class-loader settings.
The problem was that the xml-apis and javax.xml jars were being imported over some maven dependencies.
Since we already set the class loader policies for the application to PARENT_LAST, the javax.xml.transform.TransformerFactory was being loaded from the WebApp-Class loader from our jar files.
However its implementation 'com.ibm.xtq.xslt.jaxp.compiler.TransformerFactoryImpl' was coming from the server class loader, this one was linked to the javax.xml.transform.TransformerFactory provided by the JDK/JRE.
Since the classes were loaded from different sources a ClassCastException was thrown.
Removing all dependencies to xml-apis / xerces / javax.xml jars solved the problem.
Since these APIs are now part of the JDK they no longer need to be imported.
... and if you wonder why I know so much about this issue: I work together with Aaron. ;)
I can't speak for Restrict as I have no personal experience with it,But I think the problem is more to do with IBM Class Loader. The class you are referring to is part of IBM Java implementation of TransformerFactory, I think you can try one of the following to solve this issue on hand
Either change the server class loader policy to PARENT_LAST (This way class loader will find the class from application's local class path, before going to up the chain all the way to java run time)
The other option would be look at the jaxp.properties file, I think it is located in (was_root\java\jre\lib), I only read about this option never actually used it
Why do you say IBM classes are leaking into your application?
The TransformerFactory is asked to create a newInstance. It follows a sequence of steps to determine which TransformerFactory to use. If none of the config is specified, it simply chooses to use the default factory.
Here is the javadoc for TransformerFactory:
http://download.oracle.com/javase/1.5.0/docs/api/javax/xml/transform/TransformerFactory.html#newInstance()
What is the OS ? Is that AIX?
http://www.ibm.com/developerworks/java/jdk/aix/j664/sdkguide.aix64.html
Looking at this doc (link above) for AIX it tells me that this is the default Impl:
javax.xml.transform.TransformerFactory
Selects the XSLT processor. Possible values are:
com.ibm.xtq.xslt.jaxp.compiler.TransformerFactoryImpl
Use the XL TXE-J compiler. This value is the default.
Post back additional information so that we can try and troubleshoot this.
HTH
Manglu

Tapestry5 : No service implements the interface org.springframework.context.ApplicationContext

I'm using the Tapestry5 tapx template library to send an html email, as per this example.
When I run the example I get the following error:
Caused by: java.lang.RuntimeException: No service implements the interface org.springframework.context.ApplicationContext.
at org.apache.tapestry5.ioc.internal.RegistryImpl.getService(RegistryImpl.java:560)
at org.apache.tapestry5.ioc.internal.ObjectLocatorImpl.getService(ObjectLocatorImpl.java:44)
All the tapestry-* jars, including tapestry-spring-5.1.05.jar are in my classpath.
Any clues as to what I'm missing?
Figured it out. SpringIOC loads all modules it find on the classpath. The SpringModule, in tapestry-spring.jar, attempts to initialise the ApplicactionContext service, which causes the problem.
Removing tapestry-spring.jar from the classpath fixes the problem.
Follow the directions on the web site carefully; my guess is that you are not using the special TapestrySpringFilter (instead of the normal TapestryFilter).
It's been a while since I looked at this code; I can't remember if the ApplicationContext is exposed as a service or injectable object. Seems like it should be.
Fair enough; not sure what you situation is, but you should look in more detail at what TapestrySpringFilter does in terms of set up and replicate it into your standalone app's startup. There's some special bootstrapping magic that you will want to leverage.

Resources