If you consider using EclipseLink as the JPA provider for your application, give the area of weaving careful thought. Weaving is a bytecode manipulation technique that EclipseLink uses for various optimizations in the handling of JPA entities, including lazy loading of relations and attributes, and change detection. While you could, in principle, run EclipseLink completely without bytecode weaving, you wouldn't do so in a production environment as it improves the performance of your application significantly.
SAP NetWeaver AS Java doesn't allow any bytecode modifications at class loading time, so you have to perform the weaving earlier when actually compiling the application – a technique referred to as "static weaving". EclipseLink provides an Ant task for that purpose, which you can integrate into the build process. Users of the SAP NetWeaver Development Infrastructure (NWDI) might find this task rather challenging, mainly because the knowledge about the build extension mechanism isn't widely spread. In this blog, I'll show you step-by-step how to include EclipseLink's static weaving into the NWDI component build.
Note: In more general terms you can consider \ this description an example for integrating an arbitrary Ant task into the NWDI component build.
In the NWDI, each Development Component (DC) has a dependency to a particular Build Plugin DC that implements the build process for that DC type. This dependency is set automatically when you create a new DC in the SAP NetWeaver Developer Studio.
A Build Infrastructure Extension DC allows you extend the predefined build process, thus adapting it to your specific needs. To enable these changes for the build of a particular DC, simply add a build-time dependency on the Build Infrastructure Extension DC to your DC. The Build Infrastructure Extension can contain Java code or Apache Velocity macros. An extension DC also contains a special descriptor file for the build plugin framework to define the extension.
As a prerequisite for integrating EclipseLink's weaving into the NWDI component build, you should get an EclipseLink archive (including the matching javax.persistence API) of exactly the same version that is deployed on your application server. Note that a version mismatch might cause problems with the woven classes. You can download EclipseLink from this location: http://www.eclipse.org/eclipselink/downloads/index.php
The eclipselink.jar file contains an Ant task that can weave a set of already compiled java classes: org.eclipse.persistence.tools.weaving.jpa.StaticWeaveAntTask. See EclipseLink's weaving documentation for details on how to use the Ant task.
In order to integrate the static weaving with the NWDI component build, you have to create a Build Infrastructure Extension DC, which is invoked immediately after the regular Java build and which simply calls EclipseLink's weaving task:
As a result, you should have a folder structure like this:
<?xml version="1.0" encoding="UTF-8"?>
<technologies version="1">
<technology name="demo.sap.com/el_static_weaving">
<uses>sap.com/tc/bi/javatech</uses>
<generators>
<extension
type="demo.sap.com~el_static_weaving"
extends="sap.com~javac"
implementation-type="macro"
implementation-after="EclipseLinkWeaving"/>
</generators>
</technology>
</technologies>
This tells the Build Plugin Framework to execute a macro called EclipseLinkWeaving with a predefined signature after each invocation of the Java compiler.
#macro(EclipseLinkWeaving $input $output $used $params)
## define task
## Use antBase to avoid duplicate definitions and
## to put the definition in the project itself.
$antBase.taskdef("staticweaver", "org.eclipse.persistence.tools.weaving.jpa.StaticWeaveAntTask")
## find persistence.xml in source folders
#set($persistenceXMLpath = false)
#set($inputpaths = $input.get("default"))
#foreach($inputpath in $inputpaths)
#if ($dc_util.isFile("${inputpath}/META-INF/persistence.xml"))
#set($persistenceXMLpath = $inputpath)
#end
#end
## copy persistence.xml and orm.xml to output folder
## static weaver needs them
#set ($outputpath = $output.get("default"))
<copy todir="${outputpath}/META-INF" verbose="true" failonerror="true">
<fileset dir="${persistenceXMLpath}/META-INF">
<include name="persistence.xml"/>
<include name="orm.xml"/>
</fileset>
</copy>
## invoke staticweaver ant task
## loglevel set toFINEST to produce some output
#StartTimer()
<staticweaver
source="${outputpath}"
target="${outputpath}"
loglevel="FINEST">
<classpath>
#set ($usedpaths = $used.get("default"))
#foreach($usedpath in $usedpaths)
<path location="${inputpath}"/>
#end
</classpath>
</staticweaver>
#ShowTimer("EclipseLink static weaving")
## remove persistence.xml and orm.xml to avoid build warning (duplicates)
<delete verbose="true">
<fileset dir="${outputpath}/META-INF">
<include name="persistence.xml"/>
<include name="orm.xml"/>
</fileset>
</delete>
#end
This macro renders the <staticweaver> task to an Ant build script using the same default input path (i.e. package folders defined for the DC), the same default used path (or "classpath") and the same output path that the Java compiler used.
For more info on the Velocity Template Language, refer to http://velocity.apache.org/.
Once you have defined the Build Infrastructure Extension for EclipseLink's weaving, you can use it to add static weaving to the build of your JPA application DC:
[copy] Copying 1 file to C:\\temp\\nwds\\workspace.jdi\\LocalDevelopment\\t\\1E783F30932B07B16D7E78DDA43001D5\\classes\\META-INF
[copy] Copying C:\\temp\\nwds\\workspace.jdi\\LocalDevelopment\\DCs\\demo.sap.com\\my_jpa_app\\_comp\\ejbModule\\META-INF\\persistence.xml
to C:\\temp\\nwds\\workspace.jdi\\LocalDevelopment\\t\\1E783F30932B07B16D7E78DDA43001D5\\classes\\META-INF\\persistence.xml
[staticweaver] [EL Finest]: 2010-08-05 14:03:33.857
--ServerSession(16527316)--Thread(Thread[Worker-17,5,main])--property=eclipselink.jpa.uppercase-column-names; default value=false
[staticweaver] [EL Finer]: 2010-08-05 14:03:33.873
--ServerSession(16527316)--Thread(Thread[Worker-17,5,main])--Searching for default mapping file in file:/C:/temp/nwds/
workspace.jdi/LocalDevelopment/t/1E783F30932B07B16D7E78DDA43001D5/classes/
[staticweaver] [EL Finer]: 2010-08-05 14:03:33.873
--ServerSession(16527316)--Thread(Thread[Worker-17,5,main])--Searching for default mapping file in file:/C:/temp/nwds/
workspace.jdi/LocalDevelopment/t/1E783F30932B07B16D7E78DDA43001D5/classes/
[staticweaver] [EL Config]: 2010-08-05 14:03:33.998
--ServerSession(16527316)--Thread(Thread[Worker-17,5,main])--The access type for the persistent class [class entities.EntityB]
is set to [FIELD].
[staticweaver] [EL Config]: 2010-08-05 14:03:34.606
--ServerSession(16527316)--Thread(Thread[Worker-17,5,main])--The access type for the persistent class [class entities.EntityA]
is set to [FIELD].
[staticweaver] [EL Config]: 2010-08-05 14:03:34.606
--ServerSession(16527316)--Thread(Thread[Worker-17,5,main])--The target entity (reference) class for the one to one mapping
element [field b] is being defaulted to: class entities.EntityB.
[staticweaver] [EL Config]: 2010-08-05 14:03:34.606
--ServerSession(16527316)--Thread(Thread[Worker-17,5,main])--The alias name for the entity class [class entities.EntityB]
is being defaulted to: EntityB.
[staticweaver] [EL Config]: 2010-08-05 14:03:34.637
--ServerSession(16527316)--Thread(Thread[Worker-17,5,main])--The column name for element [field id] is being defaulted
to: ID.
[staticweaver] [EL Config]: 2010-08-05 14:03:34.637
--ServerSession(16527316)--Thread(Thread[Worker-17,5,main])--The alias name for the entity class [class entities.EntityA]
is being defaulted to: EntityA.
[staticweaver] [EL Config]: 2010-08-05 14:03:34.637
--ServerSession(16527316)--Thread(Thread[Worker-17,5,main])--The column name for element [field id] is being defaulted to:
ID.
[staticweaver] [EL Config]: 2010-08-05 14:03:34.668
--ServerSession(16527316)--Thread(Thread[Worker-17,5,main])--The primary key column name for the mapping element
[field b] is being defaulted to: ID.
[staticweaver] [EL Config]: 2010-08-05 14:03:34.668
--ServerSession(16527316)--Thread(Thread[Worker-17,5,main])--The foreign key column name for the mapping element
[field b] is being defaulted to: B_ID.
[staticweaver] [EL Finer]: 2010-08-05 14:03:34.668
--ServerSession(16527316)--Thread(Thread[Worker-17,5,main])--Class [entities.EntityB] registered to be processed by weaver.
[staticweaver] [EL Finer]: 2010-08-05 14:03:34.668
--ServerSession(16527316)--Thread(Thread[Worker-17,5,main])--Class [entities.EntityA] registered to be processed by weaver.
[staticweaver] [EL Finest]: 2010-08-05 14:03:34.7
--ServerSession(16527316)--Thread(Thread[Worker-17,5,main])--Begin weaver class transformer processing class
[entities/EntityA].
[staticweaver] [EL Finest]: 2010-08-05 14:03:34.715
--ServerSession(16527316)--Thread(Thread[Worker-17,5,main])--Weaved persistence (PersistenceEntity) [entities/EntityA].
[staticweaver] [EL Finest]: 2010-08-05 14:03:34.715
--ServerSession(16527316)--Thread(Thread[Worker-17,5,main])--Weaved change tracking (ChangeTracker) [entities/EntityA].
[staticweaver] [EL Finest]: 2010-08-05 14:03:34.715
--ServerSession(16527316)--Thread(Thread[Worker-17,5,main])--Weaved lazy (ValueHolder indirection) [entities/EntityA].
[staticweaver] [EL Finest]: 2010-08-05 14:03:34.715
--ServerSession(16527316)--Thread(Thread[Worker-17,5,main])--Weaved fetch groups (FetchGroupTracker) [entities/EntityA].
[staticweaver] [EL Finest]: 2010-08-05 14:03:34.715
--ServerSession(16527316)--Thread(Thread[Worker-17,5,main])--End weaver class transformer processing class [entities/EntityA].
[staticweaver] [EL Finest]: 2010-08-05 14:03:34.715
--ServerSession(16527316)--Thread(Thread[Worker-17,5,main])--Begin weaverclass transformer processing class [entities/EntityB].
[staticweaver] [EL Finest]: 2010-08-05 14:03:34.715
--ServerSession(16527316)--Thread(Thread[Worker-17,5,main])--Weaved persistence (PersistenceEntity) [entities/EntityB].
[staticweaver] [EL Finest]: 2010-08-05 14:03:34.715
--ServerSession(16527316)--Thread(Thread[Worker-17,5,main])--Weaved change tracking (ChangeTracker) [entities/EntityB].
[staticweaver] [EL Finest]: 2010-08-05 14:03:34.715
--ServerSession(16527316)--Thread(Thread[Worker-17,5,main])--Weaved fetch groups (FetchGroupTracker) [entities/EntityB].
[staticweaver] [EL Finest]: 2010-08-05 14:03:34.715
--ServerSession(16527316)--Thread(Thread[Worker-17,5,main])--End weaver class transformer processing class [entities/EntityB].
[timer] EclipseLink static weaving finished in 1.045 seconds
[delete] Deleting 1 files from C:\\temp\\nwds\\workspace.jdi\\LocalDevelopment\\
t\\1E783F30932B07B16D7E78DDA43001D5\\classes\\META-INF
[delete] Deleting C:\\temp\\nwds\\workspace.jdi\\LocalDevelopment\\
t\\1E783F30932B07B16D7E78DDA43001D5\\classes\\META-INF\\persistence.xml
A slight warning: Check the build log, especially the[staticweaver] output, carefully. If EclipseLink didn't detect any entities (for example due to a missing persistence.xml file), the build would still succeed but the entities wouldn't be woven – and thus not be mentioned in the [staticweaver] log.
If you have a SAP Service Marketplace user, you can refer to SAP Support Note 1061467 to get more information on the Build Plugin Framework.
That's basically all you have to do to add EclipseLink's static weaving to the NWDI component build:
Admittedly, creating the Build Infrastructure Extension is a bit tricky, but it's a one-time-only task. Once you have the definition in place, you can use it over and over again for all your EclipseLink applications.