Talking with Doug Clarke of EclipseLink fame and fortune last week, it sounds like there is some real interest from developers in wanting to update WebLogic Server to use later versions of the EclipseLink in order to access it's evolving feature set.
Which should offer no surprises really, EclipseLink rocks.
Investigation
Turning to the situation at hand, the main points to be addressed are:
1. The later versions of EclipseLink are JPA 2.0 based, so we'll assume that the predominant use case is centered around using JPA 2.0.
WebLogic Server supports JPA 2.0 through the application of a Smart Update patch or via manual adjustments to the PRE_CLASSPATH to incorporate two additional JAR files that enable the use of JPA 2.0.
We'll consider this one easy to handle using documented features.
2. WebLogic Server provides a version of EclipseLink that is loaded as one it's standard feature bearing modules and thus is present by default in the classpath of WebLogic Server for deployed applications. For WLS 10.3.5, this version is org.eclipse.persistence_1.1.0.0_2-1.jar.
WebLogic Server has a feature called the Filtering Classloader, which enables applications to selectively override the libraries from WebLogic Server that an application sees. This should allow an application to be configured to not use the default version of EclipseLink that WebLogic Server provides. This requires each application to specifically provide a weblogic-application.xml file that lists the <prefer-application-packages> configuration set to explicitly filter our the org.eclipselink.persistence package.
3. Any change to the EclipseLink version should be isolated to just an application, and not applied to an entire WebLogic Server installation or domain.
To make the later version of EclipseLink available, there are a few simple options available that could be explored: a) the EclipseLink jar file could be added to the CLASSPATH of WebLogic Server; b) the EclipseLink jar file could be dropped into the $domain/lib directory; c) the newer version of EclipseLink could be used to replace the existing EclipseLink jar file shipped with WebLogic Server, retaining the same name; d) the WebLogic Server shared-library mechanism could be used to deploy the EclipseLink libraries which applications can then selectively reference.
For the sake of expediency, I won't bother going through the pros/cons with each of those options and will just pick a winner from my perspective: the use of a shared-library to provide a selectively consumable version of EclipseLink.
Let's just examine this for a moment -- a WebLogic Server shared-library is an artifact that can be deployed to a WebLogic Server target, which can then be referenced by an application being deployed, whereupon WebLogic Server will merge the contents of the shared-library with the application. This enables common libraries to be deployed and used by multiple applications. Furthermore, shared-libraries can take the format of a standard Java EE archives, where descriptors can be provided which are then also merged with the final application deployment.
Given those capabilities:
a) it's possible to construct and deploy an EAR file based shared-library that contains a later version of EclipseLink and a weblogic-application.xml file which provides a preset prefer-application-packages setting that filters the org.eclipselink.persistence.* package.
b) to use a later version of EclipseLink, an application simply needs to include it's own weblogic-application.xml that imports the EclipseLink shared-library it need to use.
Thus, we have a supported deployment format (can be targeted at single nodes, clusters, whatever ...) to provide later versions of EclipseLink, which can be shared and selectively used by applications as desired.
Implementation
To test this out in an end-to-end manner, I performed the following steps:
1. Downloaded eclipselink-2.2.0.v20110202-r8913.zip from the EclipseLink web site.
2. Created a small ant project to produce an eclipselink-shared-lib.ear file. The layout of the shared-library is just a standard Java EE EAR file and contains the following items:
META-INF/weblogic-application.xml
META-INF/application.xml
lib/eclipselink.jar
The weblogic-application.xml file contains the following configuration elements:
<weblogic-application>
<prefer-application-packages>
<package-name>org.eclipse.persistence.*</package-name>
</prefer-application-packages>
</weblogic-application>
The application.xml was a necessary element to support the runtime library merging. As you can see from the below, it's basically a NOOP configuration file.
<application>
<display-name>eclipselink-shared-lin</display-name>
<module>
<java></java>
</module>
</application>
The ant build script produces an EAR file from these elements with one important addition that marks the EAR file as a shared-library for WebLogic Server by adding a number of attributes to the META-INF/MANIFEST.MF file:
<target name="package" depends="prepare">
<jar destfile="dist/${ant.project.name}.ear">
<metainf dir="etc" includes="*.xml"/>
<manifest>
<attribute name="Extension-Name" value="eclipselink"/>
<attribute name="Specification-Version" value="2.0"/>
<attribute name="Implementation-Version" value="2.2.0"/>
</manifest>
<fileset dir="build" includes="**/*"/>
</jar>
</target>
At deployment time, WebLogic Server will use the attributes as meta-data for the deployed shared-library.
The final EAR file looks like this:
sbutton:~/Projects/Java/eclipselink-shared-lib/dist $ jar tf eclipselink-shared-lib.ear
META-INF/
META-INF/MANIFEST.MF
META-INF/application.xml
META-INF/weblogic-application.xml
lib/
lib/eclipselink.jar
For reference, the simple ant project to build the eclipselink-shared-lib.ear file is here: eclipselink-shared-lib.zip.
3. Deployed eclipselink-shared-lib.ear to WebLogic Server. This results in a new library being available on the server, eclipselink#2.0@2.2.0.
4. Created a test application that imports eclipselink#2.0@2.2.0 and outputs the version of it that it is seeing.
The application uses a weblogic.xml to reference the eclipselink#2.0@2.2.0 shared-library that was deployed, which picks up both the new version of eclipselink.jar as well as the filtering-classloader description the library contains:
weblogic-application.xml:
<weblogic-application>
<library-ref>
<library-name>eclipselink</library-name>
<specification-version>2.0</specification-version>
<implementation-version>2.2.0</implementation-version>
</library-ref>
</weblogic-application>
Within the application, used a simple servlet that outputs the version of EclipseLink it is seeing:
out.printf("<p>EclipseLink Version: %s</p>", org.eclipse.persistence.Version.getVersionString());
5. Packaged the application into an EAR file and deployed it to WebLogic Server as an application.
When the application is accessed it reports the new version of EclipseLink supplied via the shared-library:
EclipseLink Version: 2.2.0.v20110202-r8913
6. Performed a negative test by undeploying the application, removed the weblogic-application.xml file from it and redeployed it.
When the application is accessed it reports the default version of EclipseLink that WebLogic Server supplies:
EclipseLink Version: 2.1.3.v20110304-r9073
7. With the basic premise validated, add a JPA module to validate the code-weaving EclipseLink performs works as expected. To verify the version EclipseLink is using the EclipseLink log level was set to fine and the console output reviewed, which showed up as Eclipse Persistence Services - 2.2.0.v20110202-r8913
Summary
The use of WebLogic Server shared-libraries appears to be a very suitable model for providing later versions of EclipseLink that automatically filter out the WebLogic Server supplied versions, which applications can selectively choose to import when they need the later versions.
This was just a simple test of the concept of using WebLogic Server shared-libraries to do this. The EclipseLink/TopLink team are in the throes of formally certifying this approach, so keep an eye out for it on the EclipseLink site if it does pass full muster!
Showing posts with label EclipseLink. Show all posts
Showing posts with label EclipseLink. Show all posts
13 October 2009
Exploring EclipseLink @OptimisticLocking
Been a while since I've had some fun with JPA, so I decided to spend a little time with it today.
I created a very simple domain model (Employee --> LeaveRecord) to use.
Since I was just intent of doing some quick testing, instead of following the usual route of creating an EJB session facade to expose the @Entity objects, and then exercising that from a client to test things out, I simply created some JUnit4 @Test cases to act as the test clients. These @Test cases exercised the @Entity objects from outside the container, so it was actually a very easy way to go.
One thing I'd never looked at much was the EclipseLink specific annotations, so I decided to take a quick peek around there for something interesting to test. A quick peruse of the EclipseLink documentation drew me to the @OptimisticLocking annotation. How could you not be optimistic with that!
The goal of the @OptimisticLocking annotation is to direct EclipseLink to use an optimistic locking strategy for the @Entity, directing it to the current property values from the object it is persisting against the data currently in the database to ensure it hasn't changed since it was last read.
There are several different options available, so I took a look at the differences between the OptimisticLockingType.ALL_COLUMNS and the OptimisticLockingType.CHANGED_COLUMNS options.
The @OptimisticLocking annotation is specified on the POJO.
@Table(name = "EMPLOYEES")
@OptimisticLocking(type=OptimisticLockingType.ALL_COLUMNS)
public class Employee implements Serializable {
...
} Doing simple reads and updates of the Employee @Entity with the eclipselink.logging.level set to FINEST shows the SQL that is created when the different types are applied.
@OptimisticLocking(type=OptimisticLockingType.ALL_COLUMNS)
Connection(26174809)--UPDATE EMPLOYEES SET VACATION_HOURS = ?
WHERE ((EMPLOYEE_ID = ?) AND
(((((EMAIL_ADDRESS = ?) AND
(FIRST_NAME = ?)) AND
(LAST_NAME = ?)) AND
(SALARY = ?)) AND
(VACATION_HOURS = ?)))
bind => [999, 1, jack.rooster@anon.org1, Jack1, Rooster1, 1.0, 1]
In this configuration, all the fields of the @Entity are contained in the WHERE clause of the UPDATE statement.
@OptimisticLocking(type=OptimisticLockingType.CHANGED_COLUMNS)
Connection(2554341)--UPDATE EMPLOYEES SET VACATION_HOURS = ?
WHERE ((EMPLOYEE_ID = ?) AND (VACATION_HOURS = ?))
bind => [999, 1, 1]
In this configuration, only the updated fields of the @Entity are contained in the WHERE clause of the UPDATE statement.
After testing the @OptimisticLocking annotation and observing that I worked as expected in my test environment, the next step was to test what happens when a change is made to an object after it has been read, but before it is updated.
The flow is essentially this:
T1 --> read employee 1
T1 --> create and start T2
T2 --> read employee 1
T2 --> update employee 1
T2 --> persist employee 1
T1 --> update employee 1
T1 --> persist employee 1 *expect OptimisticLockingException*
The @Test case below represents this sequence.
@Test(expected = RollbackException.class, timeout = 20000)
public void checkOptimisticLocking() throws Exception {
Employee pre = employeePM.find(Employee.class, Long.valueOf(1));
// do the separate thread update of the specified employee with value
EmployeeTestOptimisticLockingHelper t =
new EmployeeTestOptimisticLockingHelper(1L, -999L);
t.start();
t.join(10000);
// Now do the local the update
// should throw OptimisticLockException
employeePM.getTransaction().begin();
pre.setVacationHours(999L);
employeePM.getTransaction().commit();
}
The EmployeeTestOptimisticLockingHelper is a separate class that is executed via another Thread. This allows it to perform the change using a separate EntityManager.
public class EmployeeTestOptimisticLockingHelper extends Thread {
private Long id;
private Long newval;
public EmployeeTestOptimisticLockingHelper(Long id, Long newval) {
this.id = id;
this.newval = newval;
}
@Override
public void run() {
EntityManager em = null;
try {
em = Persistence.createEntityManagerFactory("CompanyUnit")
.createEntityManager();
Employee emp = em.find(Employee.class, id);
em.getTransaction().begin();
emp.setVacationHours(newval);
em.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
} finally {
em.close();
interrupt();
}
}
}
When this @Test is executed, it results in the following exception being thrown, demonstrating that the specified @OptimisticLocking model is working as expected.
[EL Finer]: 2009.10.13 14:54:43.796--ClientSession(14031599)--Connection(26174809)--rollback transaction
[EL Warning]: 2009.10.13 14:54:43.796--UnitOfWork(26953544)--javax.persistence.OptimisticLockException: Exception [EclipseLink-5006] (Eclipse Persistence Services - 1.0.2 (Build 20081024)): org.eclipse.persistence.exceptions.OptimisticLockException
Exception Description: The object [Employee 1 Jack1 Rooster1 jack.rooster@anon.org1 999 $1.0] cannot be updated because it has changed or been deleted since it was last read.
Class> sab.demo.company.domain.Employee Primary Key> [1]
at org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.commitToDatabase(RepeatableWriteUnitOfWork.java:480)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabaseWithChangeSet(UnitOfWorkImpl.java:1330)
at org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.commitRootUnitOfWork(RepeatableWriteUnitOfWork.java:159)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitAndResume(UnitOfWorkImpl.java:1002)
at org.eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl.commitInternal(EntityTransactionImpl.java:84)
at org.eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl.commit(EntityTransactionImpl.java:63)
at sab.demo.testdomains.EmployeeTest.checkOptimisticLocking(EmployeeTest.java:149)
And the JUnit runner shows the @Test passes as expected, since it is configred to expect the wrapper javax.persistence.RollbackException to occur.
Subscribe to:
Posts (Atom)

