How to enable Eclipselink's static weaving fro

2019-07-02 14:35发布

问题:

I'd like to enable Eclipselink's static weaving for my JPA classes from Gradle. The Eclipselink docs explain how to do this in an Ant task:

<target name="define.task" description="New task definition for EclipseLink static weaving"/>

<taskdef name="weave" classname="org.eclipse.persistence.tools.weaving.jpa.StaticWeaveAntTask"/>
</target>
<target name="weaving" description="perform weaving" depends="define.task">
    <weave  source="c:\myjar.jar"
            target="c:\wovenmyjar.jar"
            persistenceinfo="c:\myjar-containing-persistenceinfo.jar">
        <classpath>
            <pathelement path="c:\myjar-dependent.jar"/>
        </classpath>

    </weave>
</target>

Now I've got 2 questions:

1. How do I 'translate' this into a Gradle approach? I've tried this (based on the docs at http://www.gradle.org/docs/current/userguide/ant.html#N1143F):

task eclipseWeave << {
    ant.taskdef(name: "weave",
                classname: "org.eclipse.persistence.tools.weaving.jpa.StaticWeaveAntTask",
                classpath: configurations.compile.asPath)

    ant.weave(source: relativePath(compileJava.destinationDir),
              target: relativePath(compileJava.destinationDir),
              persistenceinfo: relativePath(processResources.destinationDir) {
    }
}

but the problem is that the classpath doesn't seem to work within ant.weave(..). The weaving process is aborted after a bit with the message:

Execution failed for task ':eclipseWeave'.
> java.lang.NoClassDefFoundError: some/class/from/my/dependencies

The classpath setting works for ant.taskdef(..) since the StaticWeaveAntTask is found in my dependencies. How can I make it apply to ant.weave(..) itself?

2. How do I integrate this into my build, so it is executed automatically after each compileJava step?

回答1:

I know this is an old question, but based on the OP's comment for the "gradle" way to do it, I thought I'd share our approach. We are using the JavaExec task and the various available configuration objects.

Since the weaving is done in the classes directory (before the JAR is built) you end up only having to build one jar, not two. Since our jar is quite large, that was important to us.

task performJPAWeaving(type: JavaExec, dependsOn: "compileJava"){
  inputs.dir compileJava.destinationDir
  outputs.dir compileJava.destinationDir
  main "org.eclipse.persistence.tools.weaving.jpa.StaticWeave"
  args  "-persistenceinfo",
   "src/main/resources",
   compileJava.destinationDir.getAbsolutePath(),
   compileJava.destinationDir.getAbsolutePath()
   classpath = configurations.compile
}

tasks.withType(Jar){
  dependsOn "performJPAWeaving"
}


回答2:

Problem description

I had a similar issue and would like to share my solution which is based on the previous post. Following problems were solved:

  • Process persistence.xml and replace tokens before weaving.
  • Find a workaround for this known EclipseLink Weaver bug which prevents using the Weavers -persistenceunit flag with Gradle.
  • Discover entitiy classes automatically instead of listing them in persistence.xml.

Step 1: Process persistence.xml

Firstly, the placeholder @datasourceName@ should be replaced with an actual value. This is achieved through following snipped in your build.gradle file:

import org.apache.tools.ant.filters.ReplaceTokens

ext {
    dsName = 'MyDataSourceName'
    puName = 'MyPuName'
}

processResources {
    filter(ReplaceTokens, tokens: [
        datasourceName: dsName,
        persistenceUnitName: puName
    ])
}

Following code shows the (simplified) src/main/resources/META-INF/persistence.xml file:

<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence 
    http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">

    <persistence-unit name="@datasourceName@" transaction-type="JTA">
        <jta-data source>osgi:service/javax.sql.DataSource/(osgi.jndi.service.name=@datasourceName@)</jta-data-source>

    <!-- Necessary to let EclipseLink/Weaver discover local classes without listing them in this file,
        see http://www.eclipse.org/eclipselink/documentation/2.7/concepts/app_dev001.htm#BGBHFFAG-->
    <exclude-unlisted-classes>false</exclude-unlisted-classes>

    <properties>
        <!-- Tell the application container that our classes are already woven,
            see https://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Advanced_JPA_Development/Performance/Weaving/Static_Weaving-->
        <property name="eclipselink.weaving" value="static" />
    </properties>
</persistence-unit>

It's important to mention, that the flag exclude-unlisted-classes must be set to false in order to make the EclipseLink Weaver discovering the annotated entity classes automatically. Make also sure, that the property "eclipselink.weaving" is set to "static" which tells your application runtime, that the entity classes are already woven and do not need to be enhanced dynamically. If you work with OSGi, keep in mind to import all necessary EclipseLink packages into the bundle which contains the woven classes.

After the processResources task has been executed, the generated file build/resources/main/META-INF/persistence.xml looks like this (note the persistence-unit name):

<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
    http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">

    <persistence-unit name="MyPuName" transaction-type="JTA">
        <jta-data-source>osgi:service/javax.sql.DataSource/(osgi.jndi.service.name=MyDataSourceName)</jta-data-source>

    <!-- Necessary to let EclipseLink/Weaver discover local classes without listing them in this file, 
      see http://www.eclipse.org/eclipselink/documentation/2.7/concepts/app_dev001.htm#BGBHFFAG -->
    <exclude-unlisted-classes>false</exclude-unlisted-classes>

    <properties>
        <!-- Tell the application container that our classes are already woven,
            see, https://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Advanced_JPA_Development/Performance/Weaving/Static_Weaving-->
        <property name="eclipselink.weaving" value="static" />
    </properties>
</persistence-unit>

Step 2: Copy the generated persistence.xml

Next, we add a new Copy task which copies the processed resources into the output directory of the compileJava task. This is necessary to have all necessary artifacts on the Weavers classpath (works around bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=295031). Add following task to your build.gradle file:

task copyResourcesToClassesOutputDir(type: Copy, dependsOn: processResources) {
    from processResources.destinationDir
    into compileJava.destinationDir
}

Step 3: Weave your entity classes

In order to enhance your classes, it's necessary to configure your build appropriately. Add following snipped to your build.gradle. A separate classpath configuration has been declared because having the EclipseLink classes in the ordinary compile classpath is not desired.

configurations {
    providedApi
}

dependencies {
    providedApi 'org.eclipse.persistence:org.eclipse.persistence.jpa:2.7.2'
    providedApi 'org.eclipse.persistence:javax.persistence:2.2.0'
}

task performJPAWeaving(type: JavaExec) {
    main "org.eclipse.persistence.tools.weaving.jpa.StaticWeave"
    args "-loglevel",
        "FINE",
        compileJava.destinationDir.absolutePath,
        compileJava.destinationDir.absolutePath

    classpath (configurations.compile, configurations.providedApi)

    dependsOn compileJava
    dependsOn copyResourcesToClassesOutputDir
}

// Do always weave the classes before a JAR is created
tasks.withType(Jar) {
    dependsOn "performJPAWeaving"
}

That's it! If you run now any Jar task, your entity classes will be enhanced before they are packed into the Jar file.