Friday, October 25, 2013

Simple Mule 3.4 Component Binding Complete Example

We have been looking at Mule to help with our routing needs as part of our ongoing decomposition projects.  I have experience with other ESBs and prefer using some programmatic control, so flows can be easily componentized and then declaratively wired together.  I believe this approach balances the programmatic and declarative control and uses the strengths of each to promote flexibility and maintainability.

Mule Component Bindings seemed to be the solution I was looking for, so I started to focus my research in that area.  Unfortunately, the documentation available on the component binding approach was dated and didn't work out-of-the-box with the Mule 3.4 version, so I took some time to create a simple example to demonstrate the functionality end-to-end in a small footprint.

This Mule Component Binding Example, uses both Mule declarations and a Java class to direct the flow to the components declared in the BindTestObjectProof.xml file. To follow this example, the reader must have some basic understanding of Mule, Java, HTTP and JMS.  The example's encapsulated flow, functionality, structure, comments and component naming is purposefully simplistic and verbose to facilitate understanding.


The diagram above shows the example flow that consists of the following steps:
  • Step 1: A client makes an HTTP request to http://localhost:8081, initiating the flow.  A string is appended to the message payload and the request is directed to the TestInitiator:processRequest method called through the Mule's Default Behavior, passing in the augmented message payload.
  • Step 2: The TestInitiator:processRequest method creates a TestDao object, adding the passed-in request data to each field.  Then the TestObjectServiceInterface:repeatNameString method is called with the TestDao object, which sends a message to the "vm.ObjectDirectService" inbound-endpoint declared in the flow.
  • Step 3: The "vm.ObjectDirectService" endpoint passes the message to the TestObjectDirectService:repeatNameString method with the payload as an argument.  A String is appended to each of the TestDao fields and the message response is sent with the payload returned by the Java method.
  • Step 4: In the TestInitiator:processRequest method, the  TestObjectServiceInterface:repeatNameString method returns with the augmented TestDao object.
  • Step 5: The TestInitiator:processRequest method then calls the TestObjectServiceInterface:repeatNameJms method with the TestDao object, which sends a message to the "vm.ObjectJmsService" inbound-endpoint declared in the flow.
  • Step 6: The "vm.ObjectJmsService" endpoint uses a request-reply construct to synchronize the JMS request/response MEP and posts a message to the JMS outbound-endpoint wired to the "QueueObjectRequest" queue. 
  • Step 7: The JMS message is received by the JMS inbound-endpoint listening on the "QueueObjectRequest" queue
  • Step 8: The message is passed to the TestObjectJmsService:repeatNameJms method with the payload as an argument.  A string is appended to each of the TestDao fields and returned.
  •  Step 9: A JMS response message is generated with the payload returned by the Java method and then sent to the JMS outbound-endpoint wired to the "QueueObjectResponse" queue.
  • Step 10: The JMS response message is received by the JMS inbound-endpoint listening on the "QueueObjectResponse" queue and returned up the flow.
  • Step 11: The message is returned up the flow.
  • Step 12: In the TestInitiator:processRequest method, the  TestObjectServiceInterface:repeatNameJms method returns with the augmented TestDao object and then the TestInitiator:processRequest method also returns the augmented TestDao.  
  • Step 13: The message is returned up the flow, transformed to JSON and sent in an HTTP response to the client.

Running the Example

To share this example, I exported a Mule zip (with source code) and uploaded to my public Google Drive at MuleBindTestProof.zip.


When Running the example in Mule, you should see the following output from the command line
>curl.exe http://localhost:8081/
{"name":"mark/-000-String-JMS","address":"milpitas/-000-String-JMS","description":"testing/-000-String-JMS"}

In the mule logs/console, you should see something like the following (depending on your log level):
**START TestInitiator:processRequest - request=/-000

Return from repeatNameString - result=name=mark/-000-String; address=milpitas/-000-String, description=testing/-000-String

Return from repeatNameJms - result=name=mark/-000-String-JMS; address=milpitas/-000-String-JMS, description=testing/-000-String-JMS

**END TestInitiator:processRequest - result=name=mark/-000-String-JMS; address=milpitas/-000-String-JMS, description=testing/-000-String-JMS


Walk through of the Mule Flow Snippet (BindTestObjectProof.xml)

Declaration to Configure JMS broker used in example. Used above in Steps 7-10
<jms:activemq-connector name="Active_MQ" specification="1.1" brokerURL="vm://localhost" validateConnections="true" doc:name="Active MQ Broker" />


Declaration of the http endpoint, the message sending to the TestInitiator after appending a string and the conversion of the resultant object to JSON after the flow is exected. Described above in Steps 1 and 13 
<flow name="TestInRequest" doc:name="TestInRequest"> 

  <!-- Inbound endpoint used to initiate flow by http call to localhost:8081 --> 
  <http:inbound-endpoint exchange-pattern="request-response" host="localhost" port="8081" doc:name="HTTP Entry Point" />   
  <!-- Route to Java flow Initiator --> 
  <vm:outbound-endpoint path="vm.TestInitiator" exchange-pattern="request-response" doc:name="Call to TestInitiator flow">   
    <!-- append request value with static value before message is sent for demo purposes --> 
    <append-string-transformer message="-000" /> 
  </vm:outbound-endpoint> 

  <!-- Transform message payload to json --> 
  <json:object-to-json-transformer doc:name="Transfor output to JSON" />
</flow>


Declaration of the TestInitiator inbound-endpoint and the TestInitiator component that is bound to the 2 outbound-endpoints through the TestObjectServiceInterface methods. Described above in Steps 2, 4, 5 and 12.
<flow name="TestInitiator" doc:name="TestInitiator"> 

  <!-- Inbound endpoint mapped to caller path above through attribute path="vm.TestInitiator" (vm.* is just a naming     convention for clarity, not a required prefix) --> 
  <vm:inbound-endpoint path="vm.TestInitiator" exchange-pattern="request-response" doc:name="Inbound endpoint for     
    Initiator" /> 

  <!-- Java class that will call the Interfaces Methods that is wired-up through the bindings --> 
  <component class="com.intuit.bind.TestInitiator" doc:name="Java Initiator"> 

    <!-- Bind the outbound endpoint to TestObjectServiceInterface:repeatNameString method which is used by TestInitiator
      (set via setTestObjectServiceInterface) --> 
   <binding interface="com.intuit.bind.TestObjectServiceInterface" method="repeatNameString"> 
     <vm:outbound-endpoint path="vm.ObjectDirectService" exchange-pattern="request-response"/> 
    </binding> 

    <!-- Bind the outbound endpoint to TestObjectServiceInterface:repeatNameJms method which is used by TestInitiator (set 
    via setTestObjectServiceInterface) --> 
    <binding interface="com.intuit.bind.TestObjectServiceInterface" method="repeatNameJms"> 
      <vm:outbound-endpoint path="vm.ObjectJmsService" exchange-pattern="request-response"/> 
     </binding> 
  </component> 
</flow>


Declaration of the vmObjectDirectService inbound-endpoint. Described above in Step 3.
<flow name="ObjectDirectService" doc:name="ObjectDirectService"> 

  <!-- Inbound endpoint mapped through the path attributes called via binding of 
    TestObjectServiceInterface:repeatNameString --> 
  <vm:inbound-endpoint path="vm.ObjectDirectService" exchange-pattern="request-response" 
    doc:name="Inbound endpoint" /> 

  <!-- Call to java class to modify object, but this could be anything (e.g. call to external service) --> 
  <component class="com.intuit.bind.TestObjectDirectService" doc:name="Java" /> 
</flow>


Declaration of the JMS Service "vm.ObjectJmsService" inbound-endpoint and the JMS message orchestration. Described above in Steps 6, 7, 10 and 11.
<flow name="ObjectJmsService" doc:name="ObjectJmsService"> 

  <!-- Inbound endpoint mapped through the path attributes called via binding of 
    TestObjectServiceInterface:repeatNameJms --> 
  <vm:inbound-endpoint path="vm.ObjectJmsService" exchange-pattern="request-response" doc:name="Inbound endpoint" /> 

  <!-- Request-reply construct to synchronize the JMS request/response MEP --> 
  <request-reply> 

    <!-- Outbound endpoint to send JMS request --> 
    <jms:outbound-endpoint connector-ref="Active_MQ" queue="QueueObjectRequest" /> 

    <!-- Inbound endpoint to receive JMS response --> 
    <jms:inbound-endpoint connector-ref="Active_MQ" queue="QueueObjectResponse" /> 

 </request-reply> 
</flow>


Declaration of the JMS inbound-endpoint described above in Steps 8 and 9
<flow name="ObjectJmsEndpoint" doc:name="ObjectJmsEndpoint"> 

  <!-- JMS Inbound endpoint mapped through the queue attributes called via jms:outbound-endpoint --> 
  <jms:inbound-endpoint exchange-pattern="request-response" queue="QueueObjectRequest" 
    connector-ref="Active_MQ" doc:name="JMS Inbound endpoint" /> 

  <!-- Call to java class to modify object, but this could be anything (e.g. call to external service) --> 
  <component class="com.intuit.bind.TestObjectJmsService" doc:name="Java" /> 
</flow>


Walk through the Java Class Snippet (TestInitiator.java)

// instance variable that holds the interface used to invoke the outbound-endpoints
// wired-up declaratively
private TestObjectServiceInterface testObjectService;

  // Method called through the Mule's Default Behavior, passing in the augmented message payload.
  public TestDao processRequest(String request) {

  // create object to send
  TestDao dao=new TestDao("mark" + request, "milpitas" + request, "testing" + request);

  // set object to service that will append "-String" to each variable
  TestDao result = getTestObjectServiceInterface().repeatNameString(dao);

  // set object to service that will append "-JMS" to each variable
  result = getTestObjectServiceInterface().repeatNameJms(result);
  return result;
}
// This mutator is called by Mule to pass in the interface used to invoke the outbound-endpoints
// wired-up declaratively
public void setTestObjectServiceInterface(TestObjectServiceInterface testObjectService) {
  this.testObjectService = testObjectService;
}

public TestObjectServiceInterface getTestObjectServiceInterface() {
  return testObjectService;
}



Conclusion

By using both programmatic and declarative methodologies it enables the creation of reusable components at different levels of granularity, easily re-routable flows and to program using strong types where desired.

We are in the process of performance testing Mule and its routing capabilities.  Depending on the result, we may be using it as a central piece of our service infrastructure going forward.

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 :-)