Monday, November 12, 2012

Running Dojo DOH tests headlessly with PhantomJS...

One of the challenges was to run our tests in the continuous integration environment. This would enable emailing developers if a check-in caused tests to fail. Our service tests are written using Junit/HttpClient and this was a relatively easy task to complete. But our UI tests are written in Dojo's DOH and the same task posed a little more of a challenge.

We looked at PhantomJS, Zombie.js and HTMLUnit to see which one would solve our problem and we could quickly adopt. We found that PhantomJS seemed to work the best when using the DOH Runner. Zombie.js couldn't seem to load the DOH Runner correctly and HTMLUnit worked for some simple tests, but gave numerous errors while running.

PhantomJS Script
The PhantomJS script to initiate the DOH Runner turned out to be straight-forward using the the Quick Start and Reference API documentation.

phantomDohRunner.js
    var page = require('webpage').create(),
    url, text;

    if (!require('system').isSSLSupported) {
        console.log('Need SSL');
        phantom.exit();
    } else {
        console.log('Have SSL');
    }
       
    // set support for cookies
    phantom.cookiesEnabled = true;
    var debugx=false;
    
    page.onConsoleMessage = function (msg) {
        console.log(msg);
    };
    
    page.onError = function(msg, trace) {
        console.log("Error Encountered: " + msg);
    };
    
    page.onInitialized = function() {
        page.evaluate(function() {
            document.addEventListener("DOMContentLoaded", function() {
                console.log("DOM content has loaded.");
            }, false);
        });
    };
    
    page.onResourceRequested = function(request) {
        if (debugx) console.log("Request (#" + request.id + "): " + JSON.stringify(request));
    };
    
    page.onResourceReceived = function(response) {
        if (debugx) console.log('Response (#' + response.id + ', stage "' + response.stage + '"): ' + JSON.stringify(response));
    };
    

    // must have URL to hit
    url = phantom.args[0];
    if (typeof url === "undefined") {
        console.log("Error: arg[0] must be a fully qualified URL.\nCommand format is: 'phantomjs phantomDohRunner.js \"URL\" [timeout in millis]'."); 
        console.log("For Example:\nphantomjs phantomDohRunner.js \"http://HOST/tester/dojo/util/doh/runner.html?test=dojo/tests/_base/array\" 20000\n\n");        
        // phantom.exit doesn't terminate the program, so put in conditional
        phantom.exit();
    } else {
    
        // get timeout from command line or default
        timeout = phantom.args[1];
        if (typeof timeout === "undefined") {
            timeout = 60000; // default to 1 minute
        }
        
        console.log("URL = " + url);
        console.log("Timeout = " + timeout + " milliseconds\n\n");
        
        page.open(url, function () {
    
            setTimeout(function(){
                    page.render("phantomDohRunnerFinish.png");
                    console.log("Closing down.");
                    phantom.exit();
            }, timeout);
    
        });
    }

The PhantomJS script above takes a URL of the DOH Runner and the time to wait for the tests to complete (in milliseconds).

For example: phantomjs phantomDohRunner.js "http://HOST/tester/dojo/util/doh/runner.html?test=dojo/tests/_base/array" 20000

Note: Currently, for some sites PhantomJS doesn't work well when using a secure protocol (https). We asked the PhantomJS forum and found this is a known issue and a fix has been proposed. For now the workaround is to use a client-side proxy (e.g. Fiddler) that can manage the SSL handshake and allows PhantomJS interactions. 

 The script above also renders a phantomDohRunnerFinish.png image after the timer expires.  It should look something like:



Get the log to the console
I know what you are asking, "why don't I see the log output in the console?".  The answer is that the DOH Runner redirects the output, so it doesn't display.

Now we need to get access to the output displayed in the Runner.  Since we wanted it to be able to execute the Runner correctly in the browser, we decided to resend the output to the console if it was run by a Phantom agent.

We made the following changes to the DOH js files to make this happen (diff'ed output below):

util/doh/_parseURLargs.js (keep original console ref when run from DOH Runner)
1,4d0
< // need to keep original window console to log for headless runners like phantomjs (called from runner.html)
< if(typeof window.console !== "undefined" && typeof window.consoleOrig === 'undefined') {
<     window.consoleOrig=window.console;
< }

util/doh/runner.js (keep original console ref when run directly though doh/main)
1523,1526c1523
<       // Phantomjs - set to fix summary printing timing issue when running headlessly.
<       setTimeout(function() {
<           this._report();
<     }.bind(this), 500);
---
>       this._report();
1537,1540d1533
< // need to keep original window console to log for headless runners like phantomjs (called from runner.html & doh/main)
< if(typeof window!="undefined" && typeof window.console !== "undefined" && typeof window.consoleOrig === 'undefined') {
<     window.consoleOrig = window.console;
< }

util/doh/_browserRunner.js (if PhantomJS agent, send output to orig console too)
1,9c1
< define("doh/_browserRunner", ["dojo/main", "doh/runner", "dojo/_firebug/firebug"], function(dojo, doh) {
<
<     // check to see if we need to log to orignal console for phantomjs and so we don't double log if running in a true browser
<     var phantomRunner=false;
<     if(navigator.userAgent.toLowerCase().indexOf("phantomjs") > 0) {
<         window.consoleOrig.log("*** Found PhantomJS UserAgent ***");
<         phantomRunner=true;
<     }
<
---
> define(["dojo/main", "doh/runner", "dojo/_firebug/firebug"], function(dojo, doh) {
393,419c385,407
<         }else if(window["console"]){
<             if(console.error){
<                 doh.error = function(){
<                     sendToLogPane.call(window, arguments);
<                     console.error(Array.prototype.join.call(arguments, " "))
<                     if(phantomRunner) window.consoleOrig.log(Array.prototype.join.call(arguments, " "));
<                 };
<             }
<             if(console.debug){
<                 doh.debug = function(){
<                     sendToLogPane.call(window, arguments);
<                     console.debug(Array.prototype.join.call(arguments, " "))
<                     if(phantomRunner) window.consoleOrig.log(Array.prototype.join.call(arguments, " "));
<                 };
<             }else if(console.info){
<                 doh.debug = function(){
<                     sendToLogPane.call(window, arguments);
<                     console.info(Array.prototype.join.call(arguments, " "))
<                     if(phantomRunner) window.consoleOrig.log(Array.prototype.join.call(arguments, " "));
<                 };
<             }else{
<                 doh.debug = function(){
<                     sendToLogPane.call(window, arguments);
<                     console.log("DEBUG:"+ Array.prototype.join.call(arguments, " "));
<                     if(phantomRunner) window.consoleOrig.log("DEBUG:"+ Array.prototype.join.call(arguments, " "));
<                 };
<             }
---
>               }else if(window["console"]){
>                       if(console.error){
>                               doh.error = function(){
>                                       sendToLogPane.call(window, arguments);
>                                       console.error(Array.prototype.join.call(arguments, " "))
>                               };
>                       }
>                       if(console.debug){
>                               doh.debug = function(){
>                                       sendToLogPane.call(window, arguments);
>                                       console.debug(Array.prototype.join.call(arguments, " "))
>                               };
>                       }else if(console.info){
>                               doh.debug = function(){
>                                       sendToLogPane.call(window, arguments);
>                                       console.info(Array.prototype.join.call(arguments, " "))
>                               };
>                       }else{
>                               doh.debug = function(){
>                                       sendToLogPane.call(window, arguments);
>                                       console.log("DEBUG:"+ Array.prototype.join.call(arguments, " "));
>                               };
>                       }

Now when you re-run the command above, you should see the following output in your console and the finished image of the page will render correctly as well.

c:\dev\ui\test>phantomjs phantomDohRunner.js "http://hostserver/tester/dojo/util/doh/runner.html?test=dojo/tests/_base/array" 20000
WE HAVE SSL
URL = http://hostserver/tester/dojo/util/doh/runner.html?test=dojo/tests/_base/array
Timeout = 20000 milliseconds

DOM content has loaded.
14 tests to run in 1 groups
------------------------------------------------------------
GROUP "tests._base.array" has 14 tests to run
PASSED test: testIndexOf 1 ms
PASSED test: testIndexOfFromIndex 1 ms
PASSED test: testLastIndexOf 0 ms
PASSED test: testLastIndexOfFromIndex 0 ms
PASSED test: testForEach 0 ms
PASSED test: testForEach_str 0 ms
PASSED test: testForEach_string_callback 2 ms
PASSED test: testEvery 2 ms
PASSED test: testEvery_str 0 ms
PASSED test: testSome 1 ms
PASSED test: testSome_str 1 ms
PASSED test: testFilter 1 ms
PASSED test: testFilter_str 0 ms
PASSED test: testMap 0 ms
WOOHOO!!
------------------------------------------------------------
| TEST SUMMARY:
------------------------------------------------------------
         14 tests in 1 groups
         0 errors
         0 failures


After we took this approach, we attended a presentation given by Dylan Schiemann and he mentioned that we could have solved for logging by using Dojo Analytics.  Currently the solution above is working for us and will use it for now.

Hope this helps - Thanks - Mark :-)

Thursday, February 16, 2012

Security Issue with services that accept XML documents through DOCTYPE Entity…

Hi Guys,

We identified a service attack and devised a solution that shouldn’t require full-double parsing/reading of the request data through an interceptor, which was the initial recommendation from RedHat.

An attacker using an DOCTYPE Entity can access the local file system and export sensitive data (see example request/response below).

The main challenge was that we use RestEasy and the implementation wasn't written to allow the addition of security attributes to the parser.

We have a proposed temporary fix to the org.jboss.resteasy.plugins.providers.jaxb.JAXBXmlTypeProvider:readFrom method which uses a custom EntityResolver, the code is as follows:

JAXBContext jaxb = findJAXBContext(type, annotations, mediaType, true);
Unmarshaller unmarshaller = jaxb.createUnmarshaller();

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
dbf.setExpandEntityReferences(false);
DocumentBuilder db = dbf.newDocumentBuilder();
// set entity resolver to throw exception when enternalEntity Exists...
db.setEntityResolver(new NoDocTypeHandler());
Document doc = db.parse(entityStream);


Object obj = unmarshaller.unmarshal(doc);


// InnerClass to throw an error if externalEntities exist
class NoDocTypeHandler extends DefaultHandler {
NoDocTypeHandler() {
super();
}
public InputSource resolveEntity(String publicId, String systemId) throws SAXException {
throw new SAXNotSupportedException("External Entity Declarations are not Supported!");
}
}


We found that just setting the ExpandEntityReferences to false didn't address all our use cases. It seemed to addressed accessing a directory structure, but when accessing a specific file with a few lines, the contents was still expanded.

We have run initials tests and the results show that it is working as expected.

We have supplied the bug and the fix to RestEasy and they are working on a long-term solution and should provide a patch soon https://issues.jboss.org/browse/RESTEASY-647.

This issue isn’t specific to RestEasy or even Java, other parser/frameworks are vulnerable.

Please let me know if you have any questions…

Hope this helps - Thanks – Mark :-)


Example Attack:

REQUEST:
POST http://host/searchService HTTP/1.1
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><!DOCTYPE root [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>
<search><user>&xxe;</user></search>

RESPONSE:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><ErrorMessage>Invalid Search Criteria for user:
LocalService:*:19:544:U-NT AUTHORITY\LocalService,S-1-5-19::
NetworkService:*:20:544:U-NT AUTHORITY\NetworkService,S-1-5-20::
Administrators:*:544:544:,S-1-5-32-544::

</ErrorMessage>

Friday, October 23, 2009

Thread Contention introduced with multiple synchronization blocks on the same object, in the same method...

I am currently working on a legacy project and wanted to communicate some of the findings/enhancements as I go along.

I came across a method that incremented static fields to kept track of the number of requests and the thresholds used to send alerts about server utilization. This method had a number of synchronization blocks that used the static lock to manage access to the static variables. The method was basically in the following form:

public void method() {
DO_SOME_WORK THAT DOESN'T NEED TO BE SYNC'ED....
synchronized (this.getClass()) {
DO_SOME_CRITICAL_WORK....
}
DO_SOME_WORK THAT DOESN'T NEED TO BE SYNC'ED....
synchronized (this.getClass()) {
DO_SOME_OTHER_CRITICAL_WORK....
}
}

Looking at the code, a lock (monitor) is attained twice on the same object, which will cause contention as more threads stack up in the queue.

I believe the implementation took to heart, keep the sync blocks as small as possible, but since the same monitor (lock) is being used in the same method, a large amount of contention was introduced.

In a JVM, how threads handle wait/notifications is not mandated in the Java specification, so it is implementation specific and results may vary depending on the JVM you use.

Through inspection, the Sun JVM seems to use a queuing metaphor to handle requests for monitor control.

So basically, the flow with 3 thread could be as follows (again, the exact order can vary, but in general):
- Thread 1,2 & 3 request monitor control, respectively.
- Thread 1 gets the monitor while Thread 2 & 3 are queued.
- Thread 1 exists sync block 1 and releases control of the monitor.
- Thread 2 gets the monitor.
- Thread 1 hits sync block 2 , can't get control of the monitor and is queued.
- Thread 2 exists sync block 1 and releases control of the monitor.
- Thread 3 gets the monitor.
- Thread 2 hits sync block 2, can't get control of the monitor and is queued.
- Thread 3 exists sync block 1 and releases control of the monitor.
- Thread 1 gets the monitor.
- Thread 3 hits sync block 2, can't get control of the monitor and is queued.
- Thread 1 exists sync block 2 and releases control of the monitor.
- Thread 2 gets the monitor.
- Thread 2 exists sync block 2 and releases control of the monitor.
- Thread 3 gets the monitor.
- Thread 3 exists sync block 2 and releases control of the monitor.

Below you will find, the actual load-tested output that depicts the choreography between the sync blocks with 20 concurrent requests and 50 concurrent requests

Some of the requests finished faster than others, but as you can see some took quite a bit longer. I highlighted and labeled some of the threads that took a longer time for maximum affect.

A better implementation would have you a single synchronized block to handle the critical areas of the methods or use an efficient monitoring tool to get the same statistics...

DISCLAIMER: Yes, I know that adding the print statements exacerbated the time spent inside the sync block therefore making the times longer, but I am trying to convey what is going on inside the JVM, not the exact duration times inside the method. All instrumentation affects performance :-)

Hope this helps - Thanks - Mark :-)

_________________________________________________________________________________
This is what happens when I sent 20 concurrent requests in...
Sync 1 out - TP-Processor8 - Outside of Sync block 1 waiting to get the monitor
Sync 1 out - TP-Processor5
Sync 1 out - TP-Processor1
Sync 1 out - TP-Processor13
Sync 1 out - TP-Processor11
Sync 1 out - TP-Processor15
Sync 1 out - TP-Processor9
Sync 1 out - TP-Processor7
Sync 1 out - TP-Processor12
Sync 1 out - TP-Processor19
Sync 1 out - TP-Processor3
Sync 1 out - TP-Processor18
Sync 1 in - TP-Processor24
Sync 1 out - TP-Processor14
Sync 1 in - TP-Processor17
Sync 2 out - TP-Processor24
Sync 2 out - TP-Processor17
Sync 1 in - TP-Processor6
Sync 2 out - TP-Processor6
Sync 1 in - TP-Processor20
Sync 2 out - TP-Processor20
Sync 1 in - TP-Processor2
Sync 2 out - TP-Processor2
Sync 1 in - TP-Processor16
Sync 2 out - TP-Processor16
Sync 1 in - TP-Processor10
Sync 2 out - TP-Processor10
Sync 1 in - TP-Processor8 - Inside Sync block 1 (has monitor)
Sync 2 out - TP-Processor8 - Outside Sync block 2 waiting to get the monitor
Sync 1 in - TP-Processor5
Sync 2 out - TP-Processor5
Sync 1 in - TP-Processor1
Sync 2 out - TP-Processor1
Sync 1 in - TP-Processor13
Sync 2 out - TP-Processor13
Sync 1 in - TP-Processor11
Sync 2 out - TP-Processor11
Sync 1 in - TP-Processor15
Sync 2 out - TP-Processor15
Sync 1 in - TP-Processor9
Sync 2 out - TP-Processor9
Sync 1 in - TP-Processor7
Sync 2 out - TP-Processor7
Sync 1 in - TP-Processor12
Sync 2 out - TP-Processor12
Sync 1 in - TP-Processor19
Sync 2 out - TP-Processor19
Sync 1 in - TP-Processor3
Sync 2 out - TP-Processor3
Sync 1 in - TP-Processor18
Sync 2 out - TP-Processor18
Sync 1 in - TP-Processor14
Sync 2 out - TP-Processor14
Sync 2 in - TP-Processor24
Sync Complete - 389 - TP-Processor24
Sync 2 in - TP-Processor17
Sync Complete - 389 - TP-Processor17
Sync 2 in - TP-Processor6
Sync Complete - 405 - TP-Processor6
Sync 2 in - TP-Processor20
Sync Complete - 390 - TP-Processor20
Sync 2 in - TP-Processor2
Sync Complete - 390 - TP-Processor2
Sync 2 in - TP-Processor16
Sync 2 in - TP-Processor10
Sync Complete - 406 - TP-Processor10
Sync 2 in - TP-Processor8 - Inside Sync block 2 (has monitor)
Sync Complete - 406 - TP-Processor16
Sync Complete - 405 - TP-Processor8 - Finished method in 405 ms

_________________________________________________________________________________
This is what happens when I sent 50 concurrent requests in...
Sync 1 out - TP-Processor5- Outside Sync block 1 waiting to get the monitor
Sync 2 out - TP-Processor38
Sync 1 in - TP-Processor28
Sync 1 out - TP-Processor46
Sync 2 out - TP-Processor28
Sync 1 in - TP-Processor44
Sync 2 out - TP-Processor44
Sync 1 in - TP-Processor34
Sync 2 out - TP-Processor34
Sync 1 in - TP-Processor20
Sync 2 out - TP-Processor20
Sync 1 in - TP-Processor10
Sync 2 out - TP-Processor10
Sync 1 in - TP-Processor2
Sync 2 out - TP-Processor2
Sync 1 in - TP-Processor35
Sync 2 out - TP-Processor35
Sync 1 in - TP-Processor42
Sync 2 out - TP-Processor42
Sync 1 in - TP-Processor1
Sync 2 out - TP-Processor1
Sync 1 in - TP-Processor40
Sync 2 out - TP-Processor40
Sync 1 in - TP-Processor36
Sync 2 out - TP-Processor36
Sync 1 in - TP-Processor16
Sync 2 out - TP-Processor16
Sync 1 in - TP-Processor14
Sync 2 out - TP-Processor14
Sync 1 in - TP-Processor26
Sync 2 out - TP-Processor26
Sync 2 in - TP-Processor31
Sync Complete - 726 - TP-Processor31
Sync 1 in - TP-Processor25
Sync 2 out - TP-Processor25
Sync 1 in - TP-Processor37
Sync 1 in - TP-Processor18
Sync 2 out - TP-Processor37
Sync 2 out - TP-Processor18
Sync 1 in - TP-Processor9
Sync 2 out - TP-Processor9
Sync 1 in - TP-Processor45
Sync 2 out - TP-Processor45
Sync 1 in - TP-Processor29
Sync 2 out - TP-Processor29
Sync 1 in - TP-Processor52
Sync 2 out - TP-Processor52
Sync 1 in - TP-Processor30
Sync 2 out - TP-Processor30
Sync 1 in - TP-Processor50
Sync 1 in - TP-Processor11
Sync 2 out - TP-Processor50
Sync 2 out - TP-Processor11
Sync 1 in - TP-Processor48
Sync 2 out - TP-Processor48
Sync 1 in - TP-Processor6
Sync 2 out - TP-Processor6
Sync 1 in - TP-Processor41
Sync 2 out - TP-Processor41
Sync 1 in - TP-Processor8
Sync 2 out - TP-Processor8
Sync 1 in - TP-Processor13
Sync 2 out - TP-Processor13
Sync 1 in - TP-Processor3
Sync 2 out - TP-Processor3
Sync 1 in - TP-Processor7
Sync 2 out - TP-Processor7
Sync 1 in - TP-Processor24
Sync 2 out - TP-Processor24
Sync 1 in - TP-Processor19
Sync 1 in - TP-Processor21
Sync 2 out - TP-Processor19
Sync 2 out - TP-Processor21
Sync 1 in - TP-Processor12
Sync 2 out - TP-Processor12
Sync 1 in - TP-Processor51
Sync 2 out - TP-Processor51
Sync 1 in - TP-Processor47
Sync 2 out - TP-Processor47
Sync 1 in - TP-Processor22
Sync 2 out - TP-Processor22
Sync 1 in - TP-Processor17
Sync 2 out - TP-Processor17
Sync 1 in - TP-Processor27
Sync 2 out - TP-Processor27
Sync 1 in - TP-Processor43
Sync 2 out - TP-Processor43
Sync 1 in - TP-Processor15
Sync 2 out - TP-Processor15
Sync 1 in - TP-Processor32
Sync 2 out - TP-Processor32
Sync 1 in - TP-Processor33
Sync 2 out - TP-Processor33
Sync 1 in - TP-Processor23
Sync 2 out - TP-Processor23
Sync 1 in - TP-Processor39
Sync 2 out - TP-Processor39
Sync 1 in - TP-Processor5 - Inside Sync block 1 (has monitor)
Sync 2 out - TP-Processor5 - Outside Sync block 2 waiting to get the monitor
Sync 2 in - TP-Processor38
Sync Complete - 947 - TP-Processor38
Sync 1 in - TP-Processor46
Sync 2 out - TP-Processor46
Sync 2 in - TP-Processor28
Sync Complete - 931 - TP-Processor28
Sync 2 in - TP-Processor44
Sync Complete - 916 - TP-Processor44
Sync 2 in - TP-Processor34
Sync Complete - 916 - TP-Processor34
Sync 2 in - TP-Processor20
Sync 2 in - TP-Processor10
Sync Complete - 900 - TP-Processor20
Sync Complete - 900 - TP-Processor10
Sync 2 in - TP-Processor2
Sync Complete - 899 - TP-Processor2
Sync 2 in - TP-Processor35
Sync Complete - 899 - TP-Processor35
Sync 2 in - TP-Processor42
Sync Complete - 899 - TP-Processor42
Sync 2 in - TP-Processor1
Sync 2 in - TP-Processor40
Sync Complete - 915 - TP-Processor1
Sync Complete - 915 - TP-Processor40
Sync 2 in - TP-Processor36
Sync Complete - 915 - TP-Processor36
Sync 2 in - TP-Processor16
Sync Complete - 915 - TP-Processor16
Sync 2 in - TP-Processor14
Sync Complete - 915 - TP-Processor14
Sync 2 in - TP-Processor26
Sync Complete - 931 - TP-Processor26
Sync 2 in - TP-Processor25
Sync Complete - 773 - TP-Processor25
Sync 2 in - TP-Processor37
Sync Complete - 758 - TP-Processor37
Sync 2 in - TP-Processor18
Sync Complete - 758 - TP-Processor18
Sync 2 in - TP-Processor9
Sync Complete - 774 - TP-Processor9
Sync 2 in - TP-Processor45
Sync 2 in - TP-Processor29
Sync Complete - 773 - TP-Processor45
Sync Complete - 773 - TP-Processor29
Sync 2 in - TP-Processor52
Sync Complete - 773 - TP-Processor52
Sync 2 in - TP-Processor30
Sync Complete - 789 - TP-Processor30
Sync 2 in - TP-Processor50
Sync Complete - 773 - TP-Processor50
Sync 2 in - TP-Processor11
Sync Complete - 773 - TP-Processor11
Sync 2 in - TP-Processor48
Sync Complete - 773 - TP-Processor48
Sync 2 in - TP-Processor6
Sync 2 in - TP-Processor41
Sync Complete - 789 - TP-Processor6
Sync Complete - 805 - TP-Processor41
Sync 2 in - TP-Processor8
Sync Complete - 789 - TP-Processor8
Sync 2 in - TP-Processor13
Sync Complete - 805 - TP-Processor13
Sync 2 in - TP-Processor3
Sync Complete - 805 - TP-Processor3
Sync 2 in - TP-Processor7
Sync Complete - 805 - TP-Processor7
Sync 2 in - TP-Processor24
Sync Complete - 805 - TP-Processor24
Sync 2 in - TP-Processor19
Sync Complete - 805 - TP-Processor19
Sync 2 in - TP-Processor21
Sync Complete - 805 - TP-Processor21
Sync 2 in - TP-Processor12
Sync Complete - 805 - TP-Processor12
Sync 2 in - TP-Processor51
Sync Complete - 821 - TP-Processor51
Sync 2 in - TP-Processor47
Sync Complete - 821 - TP-Processor47
Sync 2 in - TP-Processor22
Sync 2 in - TP-Processor17
Sync Complete - 805 - TP-Processor22
Sync Complete - 805 - TP-Processor17
Sync 2 in - TP-Processor27
Sync Complete - 821 - TP-Processor27
Sync 2 in - TP-Processor43
Sync Complete - 821 - TP-Processor43
Sync 2 in - TP-Processor15
Sync Complete - 821 - TP-Processor15
Sync 2 in - TP-Processor32
Sync 2 in - TP-Processor33
Sync Complete - 821 - TP-Processor32
Sync Complete - 2036 - TP-Processor33
Sync 2 in - TP-Processor23
Sync Complete - 2052 - TP-Processor23
Sync 2 in - TP-Processor39
Sync Complete - 2052 - TP-Processor39
Sync 2 in - TP-Processor5 - Inside Sync block 2 (has monitor)
Sync Complete - 2067 - TP-Processor5 - Finished method in 2067 ms

Thursday, February 19, 2009

Using 3rd Party Libraries (JAR) from within an EJB.jar file...

Recently, people have been asking me how to include 3rd party libraries in the EAR file, so they don't have copy them to appServer/Domain lib director during deployment.

One way of doing this is include JARs in your EAR and reference them from the Manifest of your EJB.jar file. The steps are as follows:
  • Put the 3rd party JAR files in your EAR content (at the top level)
  • Edit your EJBs Manifest and update the "Class-path:" attribute to have a space (" ") separated list of the JAR names
  • Deploy your EAR to your appServer

The EAR should look like:
- EJB.jar
- 3rdpartyA.jar
- 3rdpartyB.jar

The EJB.jar's Manifest should look like:
Manifest-Version: 1.0
Class-Path: 3rdpartyA.jar 3rdpartyB.jar

Hope this helps - Thanks - Mark :-)