Reputation: 789
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?
Upvotes: 4
Views: 3509
Reputation: 1486
In kts it should be something like this, also did some special change in the code so it can handle configuration cache as well. This should work for incremental build as well.
interface A {
@get:Inject
val fs: FileSystemOperations
@get:Inject
val exec: ExecOperations
}
tasks.withType<JavaCompile>().configureEach {
val mainSS by sourceSets.main
val classes = mainSS.output.classesDirs.singleFile
val runtimeClasspath: FileCollection = configurations.runtimeClasspath.get()
val (fs, exec) = objects.newInstance<A>().let { it.fs to it.exec }
doLast {
fs.copy {
from("src/main/resources/META-INF/")
into("${classes}/META-INF/")
}
exec.javaexec {
description = "Performs EclipseLink static weaving of entity classes"
mainClass = "org.eclipse.persistence.tools.weaving.jpa.StaticWeave"
// The weaving will be performed in place if source and target point to the same location.
args = listOf(
"-persistenceinfo",
classes.toString(),
"-classpath",
runtimeClasspath.asPath,
classes.toString(),
classes.toString()
)
classpath = runtimeClasspath
}
fs.delete {
delete("${classes}/META-INF/")
}
}
}
Upvotes: 0
Reputation: 3416
Kinda late to the party but this is possible very easily with Gradle, you just need to think it through.
There are 3 steps to do in order to make this work:
persistence.xml
file from the classes source folder to avoid duplicate persistence.xml
conflicts on the classpathAlso, it's very important to hook the process up into the compileJava task's last step in order to not break Gradle's up-to-date check, otherwise you can end up in a situation where Gradle will always recompile your code no matter nothing has changed.
For a more detailed explanation, check out my article on it: EclipseLink static weaving with Gradle.
Upvotes: 0
Reputation: 2477
None of the other answers worked for me very well (possibly because of changes to Gradle - I'm using v7.4). I ended up copying my persistence.xml (with <exclude-unlisted-classes>false</exclude-unlisted-classes>
in it) into my classes directory and then running the EclipseLink Weaver. I put it as part for doLast
for the Java Compile which I think will allow Gradle to cache compilation in the normal way (although I'm not fully clear on this) - the alternative is before the Jar task but I want to make sure that local behaviour is identical to deployed behaviour for things like lazy loading.
tasks.withType(JavaCompile) {
doLast {
def path = sourceSets.main.compileClasspath
def listOfUrls = path.collect {f -> f.toURI().toURL()} as URL[]
def loader = new URLClassLoader(listOfUrls)
//Finding classes only works from the root containing persistence.xml containing directory downwards
def xmlContents = new File(sourceSets.main.resources.srcDirs.iterator().next(), 'META-INF/persistence.xml').text
def tempDir = new File(sourceSets.main.output.classesDirs.singleFile, 'META-INF')
tempDir.mkdirs()
def tempFile = new File(tempDir, 'persistence.xml')
tempFile.delete()
tempFile << xmlContents
xmlContents = null
def proc = new org.eclipse.persistence.tools.weaving.jpa.StaticWeaveProcessor(
sourceSets.main.output.classesDirs.singleFile,
sourceSets.main.output.classesDirs.singleFile
)
proc.setLogLevel(5)
proc.setClassLoader(loader)
proc.setPersistenceInfo(sourceSets.main.output.classesDirs.singleFile)
proc.performWeaving()
tempFile.delete()
tempDir.delete()
}
}
You also have to have org.eclipse.persistence:org.eclipse.persistence.jpa
as a script dependency - for me that meant adding it to buildSrc/build.gradle
but for others it might mean adding a classpath
entry to your buildscript
dependencies like you do with Legacy plugins.
I'm not happy with how I find the persistence.xml file - I'm sure there's a better way but it works for me for now and I've spent too long on this!
Upvotes: 0
Reputation: 706
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()
classpath = configurations.compileClasspath
}
tasks.withType(Jar){
dependsOn "performJPAWeaving"
}
Upvotes: 8
Reputation: 11
I had a similar issue and would like to share my solution which is based on the previous post. Following problems were solved:
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>
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
}
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.
Upvotes: 1