7

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
PaulNUK
  • 4,774
  • 2
  • 30
  • 58

3 Answers3

3

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.

eHayik
  • 2,981
  • 1
  • 21
  • 33
2

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.

Arnold Galovics
  • 3,246
  • 3
  • 22
  • 33
  • I'm unable to test this solution as I've moved on from the company that was using Eclipselink, but this looks like the best solution, so marking it as such. – PaulNUK Mar 16 '22 at 09:27
0

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.

aschoerk
  • 3,333
  • 2
  • 15
  • 29