Skip navigation
1 2 Previous Next

bhavanishankar

26 posts
                                   
           

SailFin : Develop your own application using templates:

            Writing a new test application in SailFin is made easy using the test templates. Using the test templates, one can create the following types of test apps :
           
            invite-uas : SailFin is a callee/UAS to handle the incoming INVITE dialog.
            invite-uac : SailFin is a caller/UAC and creates a INVITE dialog.
            subscribe-uas : Useful for presence applications.
            b2bua : SailFin is a B2BUA handling the call setup b/w two parties.
            proxy : SailFin is simple proxy.
            template-only : Test app with empty source files, so that one can modify for thier needs. So, this iprovides an environment (with build files, java files) in order to quickly modify/run the test app.
           
            The test apps created using the template is ready to run, and also has "nbproject" so that it can be editied/modified using NetBeans IDE.
           
            So, here we go...
           
            0. Download and install latest SailFin from https://sailfin.dev.java.net/
            1. cvs -d:pserver:guest@cvs.dev.java.net:/cvs co sailfin/saifin-tests
            2. cd sailfin/sailfin-tests/community/sipcontainer
            3. ant create-devtest; // prints help with all the info.
            4. ant create-devtest -Dtest.name=mytest -Dtest.type=invite-uas; // create a new invite-uas test app
            5. cd mytest; // you can see that everything is created including SIP servlet, build files, sipp scenario files, etc.
            6. ant all; // the test will be compiled, run, results reported.
            [Note: Before running (6), make sure you have version 3.1 of sipp copied under sailfin/sailfin-tests/lib folder]
           
            There are already many test apps under community/sipcontainer which are created using these templates.
           
            These templates not only helps the developers, but also for the end users to quickly run something after installing the SailFin server. and also can serve as a tutorial to see how to develop different kinds of SIP servlet applications.
                                   
           

This blog explains how the automatic invalidation of SIP sessions work in SailFin container.

            To give some brief introduction, following are the ways by which a SIP session can be invalidated in SailFin container:
           
            1. Explicit invalidation by the application itself i.e., by invoking session.invalidate().
           
            2. SAS (SipApplicationSession) expiration which eventually invalidates SAS and all its children, if the application does not extend the SAS expirationTime in sessionExpired listener callback.
           
            3. IWR i.e., Automatic invalidation of sessions, I would call it "container managed invalidation" which is the fastest invalidation and resource cleanup machanism. This type of invalidation happens as soon as the sip dialog terminates (eg., after processing BYE request).  In the rest of the blog I will explain more about how this particular invalidation machanism works and the how the applications can use it and get benefit out of it.
           
            Here are the details of automatic invalidation (IWR):
           
            Section #1. Enabling the automatic invalidation:
           
            (a) If the application is of SSA 1.1 version, then the automatic invalidation is enabled by default. The 1.1 application will have its sip.xml like this:
           
           
<?xml version="1.0"?>
<sip-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.jcp.org/xml/ns/sipservlet http://www.jcp.org/xml/ns/sipservlet/sip-app_1_1.xsd"
version="1.1">
.....
</sip-app>
           
            It is also possible to write 1.1 applications only using annotations without requiring sip.xml
           
            (b) If the application is of SSA 1.0 version,, then the automatic invalidation can be enabled programatically. This need to be done when processing the initial request, something like this:
           
           
doInvite/doSubscribe/doRefer(SipServletRequest req) { // initial request.
        req.getApplicationSession().setInvalidateWhenReady(true);
        req.getSession().setInvalidateWhenReady(true);
}
           
            Section #2. Once enabled, the automatic invalidation of       javax.servlet.sip.SipSession takes place under following circumstances:
           
            (a) After BYE is processed. From a application point of view, it looks something like this:
           
           
public class UASServlet { // case 1 : UAS

    doBye(SipServletRequest req) { // incoming BYE request.

        SipSession s = req.getSession();
        s.isReadyToInvalidate(); // would be false;
        s.isValid(); // would be true
        SipServletResponse resp = s.createResponse(<resp-code>);
        resp.send();

        // After the resp.send(), the SipSession is no longer valid in all cases,
        // except when <resp-code>=401/407/408.


        // when <resp-code>=401/407/408 the client might resend the BYE again,
        // so we need to retain the SipSession.
    }

}

public class UACServlet { // case 2 : UAC

    doResponse(SipServletResponse resp) {

        if (resp.getRequest().getMethod().equals("BYE")) {
// repsonse to BYE.

            SipSession s = resp.getSession();
            s.isValid(); // would be true;
            s.isReadyToInvalidate(); // would be true;

            // The SipSession would be invalidated immediately after this method finishes,
            // in all cases except when resp.getStatus() is 401/407/408.

            // when resp.getStatus()=401/407/408, we can send BYE again and expect a response,
            // so we need to retain the SipSession.
        }
    }
}

           
            (b) After NOTIFY is processed whose Subscription-State=terminated. This is what it means for an application:
           
           
class UASServlet { // case 1 : UAS

    doNotify(SipServletRequest req) { // incoming NOTITY request.

        if (req.getHeader("Subscription-State").equalsIgnorecase("terminated")) {

            SipSession s = req.getSession();             s.isReadyToInvalidate(); // would be false;             s.isValid(); // would be true             SipServletResponse resp = s.createResponse(<resp-code>);             resp.send();             // After resp.send(), the SipSession is no longer valid in all cases,
            // except when <resp-code>=401/407/408.
            // when <resp-code>=401/407/408, the client may re-send the NOTIFY again,             // so we need to retain the SipSession.         }     } } class UACServlet { // case 2 : UAC     doResponse(SipServletResponse resp) {         String reqMethod = resp.getRequest().getMethod();         String subscriptionState = resp.getRequest().getHeader("Subscription-State");         if (reqMethod.equals("NOTIFY") && subscriptionState.equalsIgnorecase("terminated")) { // resp to NOTIFY             SipSession s = resp.getSession();             s.isValid(); // would be true;             s.isReadyToInvalidate(); // would be true;             // The session would be invalidated immediately after this method finishes,             // in all cases except when resp.getStatus() is 401/407/408.             // when resp.getStatus()=401/407/408, we can send NOTIFY again and expect a response,             // so we need to retain the session.         }     } }
           
            (c) When the non-record-routing proxy finishes proxying the request. Like below:
           
           
class NonRecordRoutingProxy {

    protected void doInvite(SipServletRequest req)
            throws ServletException, IOException {
        SipSession s = req.getSession();
        if (req.isInitial()) {
            Proxy proxy = req.getProxy();
            proxy.setRecordRoute(false);
            proxy.setSupervised(true);
            proxy.proxyTo(req.getRequestURI());
        }

        // After this method returns, SipSession is invalidated immediately.

    }
}
           
            (d) Non 2xx final response is received for the initial request.
           
           
class UACServlet { // we are the Caller.

    doErrorResponse(SipServletResponse resp) {

        SipServletRequest req = resp.getRequest();
        int statusCode = resp.getStatus();

        if ((req == INVITE / SUBSCRIBE / REFER) && (300 <= status <= 699)) { // error response for INITIAL request.

            // The session will be invalidated immediately after this method returns.

        }
    }
}

           

That is about the SipSession' invalidation in the simplest possible terms.

            Now let us see when javax.servlet.sip.SipApplicationSession gets automatically invalidated. For the SipApplicationSession it is very simple : Automatic invalidation happens when the SipApplicationSession becomes completely EMPTY i.e., when all its child protocol sessions (sip & http) get invalidated & all its servlettimers expire.
           

Section #3. Controlling the automatic invalidation:
           
            When the session is about to be automatically invalidated, sessionReadyToInvalidate(...) callback is invoked in the listener. In this callback, the application can decide whether it should allow the automatic invalidation or not. For example:

           
class SipSessionListenerImpl implements SipSessionListener {

    sessionReadyToInvalidate(SipSessionEvent e) {

        SipSession s = e.getSession();

        // invoke s.setInvalidateWhenReady(false) to disallow automatic invalidation
        // i.e., SipSession will NOT be invalidated after this method returns.

        // do nothing, to allow the automatic invalidation
        // i.e, the SipSession will be invalidated when this method returns.
    }
}
           

Similar thing can be done in the SipApplicationSessionListener as well.
           
            Also, if you don't implement any of these listeners, then the container assumes that the automatic invalidation is allowed.

            Thats a very quick and most simplified description of IWR.
                                               
 This blog explains how to implement a Back-To-Back User Agent (i.e., B2BUA) in SailFin using javax.servlet.sip.B2buaHelper, where B2BUA is essentially a  SipServlet.      
           
           

Let us take the following Back-To-Back UA scenario which is typically used in the call setup. Keep this scenario in mind (or refer to it) as you go through the code in the rest of this blog.

           
   Client                SailFin               Server

                                       INVITE -------->

                                       <-- 200OK (sdp1)
    <-- INVITE (sdp1)

    200OK (sdp2) --->

    <------------ ACK

                                       ACK (sdp2) ---->

   <------------------ Media session ----------------->


   BYE ------------->

                                       BYE ----------->

                                       <-------- 200 OK

   <---------- 200 OK
           

Let us assume the following sip addresses for each SIP entities :

           
SailFin     : sip:127.0.0.1:5060
Client      : sip:127.0.0.1:5070
Server     : sip:127.0.0.1:5080
           

The initial INVITE request will be sent from web/sip/ejb component depending on the type of the application. The code below shows how to create such a request and send it out to the server. I assume that the sipFactory is injected using @Resource SipFactory sipFactory;

           
SipServletRequest inviteRequest = sipFactory.createRequest(
                         sipFactory.createApplicationSession(),"INVITE", client, server);
inviteRequest.send();
           

When the server responds with 200 OK with its SDP (lets call it "sdp1") for the above INVITE request, then we should create the second INVITE request and send it to the client. The second INVITE request should contain the "sdp1" of the server. 

           

This is done in the doSuccessResponse method of B2BUA like this:

           
    @Override
    protected void doSuccessResponse(SipServletResponse resp)
            throws ServletException, IOException {
        if (resp.getMethod().equals("INVITE")) {
            List<SipSession> sipSessions =
                    getSipSessions(resp.getApplicationSession());
            if (sipSessions.size() == 1) { // 200 OK response received from the server
                sipSessions.get(0).setAttribute("ACK", resp.createAck()); // we need ACK for later use.
                sendInviteToClient(resp);
            }
        }
    }

    private void sendInviteToClient(SipServletResponse serverResp)
            throws ServletException, IOException {
        SipServletRequest serverReq = serverResp.getRequest();
        B2buaHelper b2buaHelper = serverReq.getB2buaHelper();

        // Swap To & From headers.
        Map<String, Set<String>> headerMap = new HashMap<String, HashSet<String>>();
        Set<String> from = new HashSet<String>();
        from.add(serverResp.getHeader("From"));
        headerMap.put("To", from);
        Set<String> to = new HashSet<String>();
        to.add(serverResp.getHeader("To"));
        headerMap.put("From", to);

        SipServletRequest clientRequest =
                b2buaHelper.createRequest(serverReq, true, headerMap);
        clientRequest.setRequestURI(
                clientRequest.getAddressHeader("To").getURI());
        if (serverResp.getContent() != null) { // set "sdp1"
            clientRequest.setContent(serverResp.getContent(),
                    serverResp.getContentType());
        }
        clientRequest.send();
    }

    // Helper method.
    private List<SipSession> getSipSessions(SipApplicationSession sas) {
        List<SipSession> sipSessions = new ArrayList<SipSession>();
        Iterator<SipSession> iter =
                (Iterator<SipSession>) sas.getSessions("SIP");
        while (iter.hasNext()) {
            sipSessions.add(iter.next());
        }
        return sipSessions;
    }
           

Now when the client responds with 200 OK with its SDP (lets call it "sdp2"), then we should send ACK to both the parties. The ACK to the server should contain the "sdp2" of the client.

           

This is done again in the doSuccessResponse method of the B2BUA like this:

           
    @Override
    protected void doSuccessResponse(SipServletResponse resp)
            throws ServletException, IOException {
        if (resp.getMethod().equals("INVITE")) {

            List<SipSession> sipSessions =
                    getSipSessions(resp.getApplicationSession());
            if (sipSessions.size() == 1) { // 200 OK response received from the server
                ......
            } else { // 200 OK response received from the client
                sendAckToClient(resp);
                sendAckToServer(resp);
            }
        }
    }

    private void sendAckToClient(SipServletResponse clientResp)
            throws ServletException, IOException {
        SipServletRequest ack = clientResp.createAck();
        ack.send();
    }

    private void sendAckToServer(SipServletResponse clientResp)
            throws ServletException, IOException {
        B2buaHelper b2buaHelper = clientResp.getRequest().getB2buaHelper();
        SipSession clientSession = clientResp.getSession();
        SipSession serverSession = b2buaHelper.getLinkedSession(clientSession);
        SipServletRequest ack = (SipServletRequest) serverSession.getAttribute("ACK");
        serverSession.removeAttribute("ACK");
        if (clientResp.getContent() != null) { // set "sdp2"
            ack.setContent(clientResp.getContent(), clientResp.getContentType());
        }
        ack.send();
    }
           

With this, we are done with successfully setting up the "Media session" between the client and the server.

            Once the media session/conversation is finished, any one party will "hang up" the phone. This will result in a BYE request being sent to the SailFin server. Lets assume that the client hangs up and hence sends the BYE request. Now it is the responsibility of the B2BUA to take further action. As a first step the B2BUA should create the second BYE request and send it to the server, as shown in the code below:
           
    @Override
    protected void doBye(SipServletRequest clientBye)
            throws ServletException, IOException {
            sendByeToServer(clientBye);             clientBye.getSession().setAttribute("BYE",clientBye.createResponse(200)); // for later use.
        }
    }

    private void sendByeToServer(SipServletRequest clientBye)
            throws ServletException, IOException {
        B2buaHelper b2buaHelper = clientBye.getB2buaHelper();
        SipSession serverSession = b2buaHelper.getLinkedSession(clientBye.getSession());
        SipServletRequest serverBye = serverSession.createRequest("BYE");
        serverBye.send();
    }
           
            Once the server responds with 200 OK for the above BYE request, then we should send 200 OK response for the BYE request of the client.
           
            This is done again in the doSuccessResponse method of B2BUA like this:
           
           
    @Override
    protected void doSuccessResponse(SipServletResponse resp)
            throws ServletException, IOException {
        if (resp.getMethod().equals("INVITE")) {
            .....
        } else if (resp.getMethod().equals("BYE")) { // 200 OK response received from server.
            send200OKToClient(resp);
        }
    }

    private void send200OKToClient(SipServletResponse serverResp)
            throws ServletException, IOException {
        B2buaHelper b2buaHelper = serverResp.getRequest().getB2buaHelper();
        SipSession clientSession =
                b2buaHelper.getLinkedSession(serverResp.getSession());
        SipServletResponse clientResp =
                (SipServletResponse) clientSession.getAttribute("BYE");
        clientSession.removeAttribute("BYE");
        clientResp.setStatus(serverResp.getStatus(), serverResp.getReasonPhrase());
        log("Sending response.\n" + clientResp);
        clientResp.send();
        clientSession.getApplicationSession().invalidate();
    }

           
            Once the media session is terminated, we should invalidate the SipApplicationSession (which also invalidates all the protocol sessions associated with it).
           
            The complete source code of the B2BUA.java explained in this blog is available as part of the OnlineBank sample which is installed under samples/sipservlet/OnlineBank directory of your SailFin installation directory. The SailFin is available for download here.
 
It is very easy to use SailFin server as an Instant Messaging server (IM server). 
For that, I wrote a IM/Chat server application which is nothing but a SIMPLE SipServlet 
running on top of SailFin.

Here is how you can deploy and use my application :

    * Download, install and start SailFin - http://sailfin.dev.java.net
    * Download the application (IMServer.war) from here 
    * Deploy the application using 'asadmin deploy IMServer.war'
    * Go through the flash demo and see how to use the application.
    * Needless to mention, you need to have a SIP aware IM client (I used X-Lite).

The flash demo is worth thousand lines of description.

The complete source code of my application is available here. 
For viewing and modifying the source code, you need to have NetBeans 6.0 
and latest version of SIP Application Development plug-in

Highlights of the code:

Here are the main code snippets of the IMServer SipServlet with some basic 
description. However the complete source code is available for download. I have used 
the very familiar names in SIP world i.e., 'bob' & 'alice' for explaining the code snippets.

Sending and receiving PRESENCE information to/from buddies:

When 'bob' adds 'alice' to his Friend's list, 'bob' will send a SUBSCRIBE request to SailFin 
server requesting to send NOTIFICATIONS for any status change of 'alice'.

When 'bob' sends a SUBSCRIBE request, doSubscribe method is called. As part of 
doSubscribe, we also send a SUBSCRIBE request to 'bob' so that SailFin gets notified of 
any status change of 'bob'.

Here is the code for doSubscribe:

 
/**
     * This method is called when the Person adds a FRIEND to his      * friend's list, Also, this method is called for every FRIEND in      * Person's friend's list when the softphone starts up.
     */
    @Override
    protected void doSubscribe(SipServletRequest request) throws ServletException, IOException {
        String friendID = ((SipURI) request.getRequestURI()).getUser();
        System.out.println(className + " :: doSubscribe " + "for friend : " + friendID + ", incoming request = \n\n" + request + "\n");         /**          * Add FRIEND to the Person's friend's list. Also, store the SipSession          * of this request, so that we can send the NOTIFY using this SipSession.          */
        Person person = findPerson(request);
        if (person != null && !person.getId().equals(friendID)) {
            person.addFriendID(friendID);
            person.setSubscriptionSession(friendID, request.getSession());
        }         
        /**          * Send 200 OK.          */
        SipServletResponse resp = request.createResponse(SipServletResponse.SC_OK);
        resp.send();         
        /**          * SUBSCRIBE for the presence information of this user.          */
        subscribeForPresenceEvents(request);         
        /**          * If the friend is currently online, send his presence information          * to this person.          */
        sendPresenceInformation(PersonDB.findPersonById(friendID), person);
    }

    /**
     * Send the SUBSCRIBE request to sourceAddress of SipServletRequest.
     */
    private void subscribeForPresenceEvents(SipServletRequest request) {
        SipURI sourceAddress = getSourceAddress(request);
        try {
            SipServletRequest sReq = sf.createRequest(sf.createApplicationSession(), "SUBSCRIBE", request.getRequestURI(), sourceAddress);
            sReq.addHeader("Event", "presence");
            sReq.addHeader("Expires", "3600");
            System.out.println(className + " :: subscribeForPresenceEvents, " + "outgoing SUBSCRIBE request = \n\n" + sReq + "\n");
            sReq.send();
        } catch (Exception ex) {
            System.out.println("Unable to send SUBSCRIBE request to " + sourceAddress);
            ex.printStackTrace();
        }
    }
 
 
For the SUBSCRIBE request we sent to 'bob' in doSubscribe method, we also receive NOTIFICATIONS from 'bob' whenever he changes his status. Following is the method which handles the NOTIFICATIONS received from 'bob'. As part of this method, we send bob's status to all his friends who are currently online.

    /**
     * This method is called when someone sends the presence information.
     */
    @Override
    protected void doNotify(SipServletRequest request) throws ServletException, IOException {
        System.out.println(className + " :: " + "doNotify, incoming request = \n\n" + request + "\n");          /**          * Retrieve the presence information from request and store          * it in Person object.          */
        Person person = storePresenceInformation(request);         
        /**          * Send 200 OK.          */
        SipServletResponse resp = request.createResponse(SipServletResponse.SC_OK);
        resp.send();         
        /**          * Notify the presence information to all friends          * who are currently online.          */
        sendPresenceInformationToAllFriends(person);
    }
 
Sending and receiving InstantMessage to/from buddies: Below is the code snippet which is invoked when 'bob' sends an InstantMessage to 'alice'. This method takes care of forwarding that message to the current IP address of 'alice'.
 
/**
     * This method is called when someone types some message in his/her * Chat window. We need to direct that message to the present location * of the destination.
     */
    @Override
    protected void doMessage(SipServletRequest request) throws ServletException, IOException {
        System.out.println(className + " :: " + "doMessage, incoming request = \n\n" + request + "\n");
        String to = ((SipURI) request.getRequestURI()).getUser();
        Person p = PersonDB.findPersonById(to);
        if (p != null && p.getStatus() == Person.Status.ONLINE) {
            System.out.println("personID = " + p.getId() + ", proxying request to presentLocationURI = " + p.getPresentLocationURI());
            Proxy proxy = request.getProxy();
            proxy.proxyTo(sf.createURI(p.getPresentLocationURI()));
            /** * Send 200 OK */
            SipServletResponse resp = request.createResponse(SipServletResponse.SC_OK);
            resp.send();
        } else {
            /** * Send 404 NOT_FOUND */
            SipServletResponse resp = request.createResponse(SipServletResponse.SC_NOT_FOUND);
            resp.send();
        }
 

 

Using the converged container, the SIP component and the JavaEE components (eg., MDB) can share the resources, and the SIP functionality can be used inside the MDB.

The flash demo embedded in this blog illustrates how the SailFin converged container is used for SIP and MDB convergence.

This is what the sample application is all about : The customer can apply for a credit card online. After applying for the credit card, the customer is supposed to receive a phone call (on his SIP phone) from the bank executive. During the conversation, the bank executive will be able to access the details submitted by the customer. The sample is written in a way that, if there is only one bank executive, the simultaneous applications submitted by the different customers will be queued in the JMS Queue (SJSMQ) and processed one by one.

The sample has two web interfaces, one for the customer to apply for the credit card online, and another for the bank executive to access the customer details during the phone conversation.

In terms of the implementation, there is a Java EE application (EAR file) containing the Web component, Message Driven Bean, and a SIP component. The web component serves as the front end for Customer and Bank executive. SIP component serves as a B2B User Agent. The MDB sequentially processes the customer requests placed in the JMS Queue (SJSMQ) and initiates a phone call between the customer and bank executive.

The sample does not demonstrate the convergence between the SIP and HTTP, instead it demonstrates the convergence between SIP and EJB.

Though this sample has not been tried with the cluster topology, it is possible for the MDB running in instanceA to access the SipApplicationSession (& its child sessions) created by the JSP running in instanceB. This is possible using the high availability feature (implemented using in-memory session replication) provided by the SailFin server.

The Flash demo shows the steps of developing this application and running it. The source code of the sample is available in SailFin CVS repository (sailfin/samples/OnlineBank).

An article which covers most of the aspects of Sun Java EE Engine is published here.

The article is a good reading for the GlassFish & JBI users. The new features of the Sun Java EE Engine, available as part of GlassFish V2, have been explained in detail in the article.

 

Recently, the component name of the Java EE service engine is changed from "JavaEEServiceEngine" to "sun-javaee-engine". It is very easy for the end users to migrate their applications to the new name. All that is required is to open your existing JBI project in NetBeans, build and deploy.

These are the detailed steps:

1. Go to http://www.netbeans.org/community/releases/60/index.html
2. Click "Download Preview"
3. Select your platform and download "Full" version.
4. Open your JBI project with this new NetBeans, do a "Clean and Build Project" and "Deploy Project".

If you have Non-NetBeans projects, then the following program can be used to migrate your projects to the new name. You just need to compile it and run it. You can also modify this utility to server your purpose.


JBICompNameChangeUtil.java

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileFilter;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class JBICompNameChangeUtil {
    
    private void printUsage() {
        System.err.println("Usage: java JBICompNameChangeUtil " +
                "<root-directory-for-all-projects>");
        System.err.println("For example: java JBICompNameChangeUtil" +
                " /space/projects/jbi");
    }
    
    private void changeName(String... args) {
        if(args.length != 1) {
            printUsage();
            System.exit(1);
        }
        
        File root = new File(args[0]);
        if(!root.exists() || !root.isDirectory()) {
            printUsage();
            System.exit(1);
        }
        List fileList = new ArrayList();
        getAllImpactedFiles(root, fileList);
        for(Iterator i = fileList.iterator(); i.hasNext();) {
            File file = (File)i.next();
            try {
                updateFile(file);
            } catch(FileNotFoundException ex) {
                ex.printStackTrace();
            } catch(IOException ex) {
                ex.printStackTrace();
            }
        }
    }
    
    private void getAllImpactedFiles(File root, List fileList) {
        FileFilter myFileFilter = new FileFilter() {
            public boolean accept(File file) {
                if(file.isDirectory()) {
                    return true;
                } else {
                    String fileName = file.getName();
                    return fileName.equals("jbi.xml");
                }
            }
        };
        File files[] = root.listFiles(myFileFilter);
        File arr[] = files;
        int len = arr.length;
        for(int i = 0; i < len; i++) {
            File file = arr[i];
            if(file.isDirectory())
                getAllImpactedFiles(file, fileList);
            else
                fileList.add(file);
        }
    }
    
    private void updateFile(File file)
            throws FileNotFoundException, IOException {
        System.out.println((new StringBuilder()).append("Updating ").
                append(file.getPath()).append("...").toString());
        String fileName = file.getName();
        BufferedReader reader = new BufferedReader(new FileReader(file));
        File tempFile = File.createTempFile(fileName, "tmp");
        BufferedWriter writer = new BufferedWriter(new FileWriter(tempFile));
        String line;
        if(fileName.equals("jbi.xml")) {
            while((line = reader.readLine()) != null) {
                line = updateSEName(line);
                writer.write((new StringBuilder()).append(line).
                        append(LINE_SEPARATOR).toString());
            }
        }
        writer.close();
        reader = new BufferedReader(new FileReader(tempFile));
        writer = new BufferedWriter(new FileWriter(file));
        while((line = reader.readLine()) != null)
            writer.write((new StringBuilder()).append(line).
                    append(LINE_SEPARATOR).toString());
        reader.close();
        writer.close();
        tempFile.delete();
    }
    
    private String updateSEName(String line) {
        if(line.indexOf("JavaEEServiceEngine") != -1)
            line = line.replace("JavaEEServiceEngine", "sun-javaee-engine");
        return line;
    }
    
    private static final String LINE_SEPARATOR = 
            System.getProperty("line.separator");
    
    public static void main(String args[]) {
        JBICompNameChangeUtil instance = 
                new JBICompNameChangeUtil();
        instance.changeName(args);
    }
    
}

 

 
It is possible to self manage the JBI runtime using the self management module of the GlassFish application server. I am showing one such example here.

In this example, we
have a timer service registered with the self management module which checks whether all the required components for the deployed JBI service assemblies are UP or not. Corrective action :

    (1) Try to bring up the components required by the running JBI service assemblies.
    (2) If (1) fails, log the error in server.log. (The example can be enhanced to send an email to administrator).

Here is the MBean code which receives the notification from the self management module, does the necessary checks and takes the corrective action.

SelfManageJBIComps.java :


package com.sun.samples.appserver;

import java.util.ArrayList;
import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;
import javax.management.Notification;
import javax.management.ObjectName;

/**
 *
 * @author Bhavanishankar <bshankar@sun.com>
 *
 */

public class SelfManageJBIComps
 implements SelfManageJBICompsMBean, SelfManageJBICompsConstants {
 
 public synchronized void handleNotification(
 Notification notification,
 Object obj) {

 StringBuffer debugMessage = new StringBuffer
 ("\n\nSelfManageJBIComps :: \n");
 
 System.out.println("SelfManageJBIComps.handleNotification : " +
 notification.getClass().getName());
 
 /**
 * Allow the JBI framework to initialize itself properly.
 */
 if(firstInvocation) {
 firstInvocation = false;
 return;
 }
 
 /**
 * Step 1. Get the connection to the MBean server.
 */
 initMBeanConnection();
 
 /**
 * Step 2. Get the list of deployed service assemblies.
 */
 Object ret = invokeMBean(
 getDeploymentServiceMBeanName(),
 "getDeployedServiceAssemblies",
 new Object[]{},
 new String[]{});
 
 if(ret == null) {
 return;
 }
 String[] serviceAssemblies = (String[]) ret;
 
 /**
 * Step 3. For each service assembly which is in "Started" 
 * state, if the required component's state is not started 
 * then start the component.
 */
 
 for(String sa : serviceAssemblies) {
 debugMessage.append("\n\tProcessing ServiceAssembly " +
 sa + "...");
 /**
 * Step 3.1. Get the state of the service assembly.
 */
 ret = invokeMBean(
 getDeploymentServiceMBeanName(),
 "getState",
 new Object[] {sa},
 new String[] {"java.lang.String"});
 if(ret == null) {
 continue;
 }
 String saState = (String)ret;
 
 debugMessage.append("\n\tState of the ServiceAssembly is " +
 saState);
 
 /**
 * Step 3.2 If the state is "Started" then
 * get the list of Components for this service assembly.
 */
 if(!"Started".equalsIgnoreCase(saState)) {
 continue;
 }
 
 ret = invokeMBean(
 getDeploymentServiceMBeanName(),
 "getComponentsForDeployedServiceAssembly",
 new Object[] {sa},
 new String[] {"java.lang.String"});
 if(ret == null) {
 continue;
 }
 String[] jbiComps = (String[])ret;
 
 for(String jbiComp : jbiComps) {
 debugMessage.append("\n\n\t JBIComponent used " +
 "by " + sa + " is " + jbiComp);
 
 ret = invokeMBean(
 getAdminServiceMBeanName(),
 "getComponentByName",
 new Object[]{jbiComp},
 new String[]{"java.lang.String"});
 if(ret == null) {
 continue;
 }
 ObjectName compName = (ObjectName)ret;
 
 ret = invokeMBean(
 compName,
 "getCurrentState",
 new Object[]{},
 new String[]{});
 
 if(ret == null) {
 continue;
 }
 
 String componentState = (String) ret;
 debugMessage.append("\n\t State of the " + jbiComp +
 " is " + componentState);
 
 if(!"started".equalsIgnoreCase(componentState)) {
 debugMessage.append("\n\t Starting " + 
 jbiComp + "...");
 ret = invokeMBean(
 compName,
 "start",
 new Object[] {},
 new String[] {});
 debugMessage.append("\n\t Successfully started " +
 jbiComp);
 }
 }
 }
 System.out.println(debugMessage.toString());
 }
 
 private static MBeanServer mBeanServer;
 private static boolean firstInvocation = true;
 
 /**
 * Get the connection to the MBean server.
 */
 private synchronized void initMBeanConnection() {
 if(mBeanServer == null) {
 ArrayList<MBeanServer> mBeanServers =
 MBeanServerFactory.findMBeanServer(null);
 if(!mBeanServers.isEmpty()) {
 mBeanServer = mBeanServers.get(0);
 }
 if(mBeanServer == null) {
 System.err.println("Unable " +
 "to obtain connection to MBean server");
 }
 }
 }
 
 private Object invokeMBean(
 ObjectName mBeanName,
 String methodName,
 Object[] params,
 String[] signatures) {
 Object output = null;
 try {
 output = mBeanServer.invoke(mBeanName,
 methodName, params, signatures);
 } catch(Exception ex) {
 ex.printStackTrace();
 }
 return output;
 }
 
 private ObjectName getDeploymentServiceMBeanName() {
 ObjectName objName = null;
 try {
 objName = new ObjectName(DEPLOYMENT_SERVICE_MBEAN);
 } catch(Exception ex) {
 ex.printStackTrace();
 }
 return objName;
 }
 
 private ObjectName getAdminServiceMBeanName() {
 ObjectName objName = null;
 try {
 objName = new ObjectName(ADMIN_SERVICE_MBEAN);
 } catch(Exception ex) {
 ex.printStackTrace();
 }
 return objName;
 }
 
}


SelfManageJBICompsMBean.java :
package com.sun.samples.appserver;

import javax.management.NotificationListener;

/**
*
* @author Bhavanishankar <bshankar@sun.com>
*
*/

public interface SelfManageJBICompsMBean
extends NotificationListener {

}

SelfManageJBICompsConstants.java
:

package com.sun.samples.appserver;

/**
*
* @author Bhavanishankar <bhankar@sun.com>
*
*/
public interface SelfManageJBICompsConstants {

/**
* Set these values as per the JBI runtime being used.
*/

public String ADMIN_SERVICE_MBEAN =
"com.sun.jbi:JbiName=server,ServiceName=AdminService," +
"ControlType=AdministrationService,ComponentType=System";

public String DEPLOYMENT_SERVICE_MBEAN =
"com.sun.jbi:JbiName=server,ServiceName=DeploymentService," +
"ControlType=DeploymentService,ComponentType=System";

}

Steps to setup:

    1. Compile the above code and create a JAR file say SelfManageJBIComps.jar. Copy this JAR file to <YOUR-GLASSFISH-INSTALLATION>/lib directory.

    2. Register the above MBean with the MBean server.

asadmin create-mbean --name SelfManageJBIComps com.sun.samples.appserver.SelfManageJBIComps
   
    3. Create a self management rule and register a timer service.

asadmin create-management-rule --eventtype timer --eventproperties period=60000 --action SelfManageJBIComps SelfManageJBIComps

How to test?

    Bring down a JBI component which is required by a running JBI service assembly and wait for a minute. After a minute you can see that the JBI component is started.

 

 

 

 
                                                                           
 Contents
           

1. Introduction
           

           

2. Brief descripions of ServiceMix, GlassFish and Java EE Service Engine

           

3. Installing ServiceMix on GlassFish

           

4. Installing Java EE Service Engine to ServiceMix

           

5. Accessing the EJBs deployed on GlassFish from ServiceMix using Java EE Service Engine

           

6. More Info.

           

 

           

                                                                           
 Introduction

            This blog details the experiments I did recently to make ServiceMix run inside GlassFish application server.
           
            The other interesting thing about this integration is the ability of the GlassFish application server to allow the in-process communication between ServiceMix and Java EE components (EJBs/Servlets wrapped as webservices) using a readily available JBI component of GlassFish viz., the Java EE Service Engine.
           
            Details of the versions of the various softwares I used for this experiment :
           
                                                                                                                                                                                                                                                                                                                                                                                                                       
GlassFish Application Serverv2 b31
ServiceMix3.0
Maven2.0.4
JDK5.0
           
           

 

           
  
                                                                           
 Brief descriptions of ServiceMix, GlassFish and Java EE Service Engine
           

ServiceMix is an open source distributed Enterprise Service Bus (ESB) and SOA toolkit built on the semantics and APIs of the Java Business Integration (JBI) specification JSR 208 and released under the Apache license. More details about the ServiceMix is found at http://servicemix.org.

           

GlassFish is free and open source Java EE 5 application server. It is based on the source code for Sun Java System Application Server donated by Sun Microsystems. More details about the GlassFish is found at http://glassfish.dev.java.net.

           

Java EE Service Engine acts as a bridge between GlassFish and JBI environment for web service providers and web service consumers deployed in GlassFish. It provides numerous benefits including the following :

           
                   
  • EJBs/Servlets packaged as web services and deployed on GlassFish are transparently exposed as service providers in JBI Enviroment
  •                
  • Java EE Components - EJBs/Servlets can consume services exposed in JBI enviroment using the Java EE service engine without being aware of the underlying binding/protocol such as SOAP, JMS etc exposing the web service.
  •                
  • In-process communication between components of application server and JBI components to increase request processing speed.
  •                
  • Any component that is plugged into ESB can directly communicate with Java EE applications. For example, clients of various bindings such as SOAP or JMS can communicate with web services developed using Java EE via JBI because of Java EE Service Engine.
  •            
            The default JBI runtime bundled with GlassFish is OpenESB which is an open source, world-class enterprise service bus based on JBI technology. The details of OpenESB is found at http://open-esb.dev.java.net.
           
            This article is for those who want to use ServiceMix as their JBI runtime with GlassFish application server. So the article explains how to install ServiceMix on GlassFish and how the ServiceMix can use Java EE Service Engine to do in-process communication with the Java EE applications (web services) deployed on GlassFish.
           

 

           

                                                                           
 Installing ServiceMix on GlassFish
           

Download, install and start the latest GlassFish (if you haven't done already) from here. From now on, I will refer $glassfish_dir to GlassFish installation directory.

           

Download either the source or binary distribution of ServiceMix from here. Now we need to create ServiceMix WAR file inorder to install ServiceMix on GlassFish. From now on, I will refer $servicemix_web_dir to your servicemix-web directory which will be either examples/servicemix-web (for binary distribution) or samples/servicemix-web (for source distribution).

           

Create ServiceMix WAR file :

           
cd $servicemix_web_dir

# Do this workaround in pom.xml :
Remove
<scope>test</scope> (line 82) (Ref : Mailing List Archive )

# Change servicemix-http configuration to accept dynamic SU deployments. In $servicemix_web_dir/src/webapp/WEB-INF/applicationContext.xml, change <http:component>....</http:component> to <bean class="org.apache.servicemix.http.HttpComponent" /> (Ref : Mailing List Archive ) # creates target/servicemix-web-3.0-incubating.war
mvn install -Dmaven.test.skip=true
# Rename the WAR file as servicemix-web.war for simplicity. mv target/servicemix-web-3.0-incubating.war target/servicemix-web.war
           

Deploy ServiceMix WAR file to GlassFish :

           
$glassfish_dir/bin/asadmin deploy --port 4848 $servicemix_web_dir/target/servicemix-web.war 
           
 
                                                                           
 Installing Java EE Service Engine to ServiceMix
           

The servicemix components (I am refering to servicemix-http which is what is used in the sample application) do not wrap/unwrap the WSDL1.1 defined messages as explained in 5.5.1.1.4 of JBI 1.0 specification. This seems like a bug with the servicemix components. So, to workaround this and make Java EE Service Engine inter-operate with servicemix components, we need to set com.sun.enterprise.jbi.se.esbruntime=servicemix system property.

           

Here are the steps :

           
# Add the following JVM option in $glassfish_dir/domains/domain/config/domain.xml and Restart GlassFish

<jvm-options>-Dcom.sun.enterprise.jbi.se.esbruntime=servicemix</jvm-options>

#
Drop the Java EE Service Engine JAR file and the required shared libraries to autodeploy directory of ServiceMix cd $glassfish_dir/domains/domain1/applications/j2ee-modules/servicemix-web/deploy
cp $glassfish_dir/jbi/sharedlibraries/wsdl/installer/wsdlsl.jar .
cp $glassfish_dir/jbi/components/javaeeserviceengine/installer/appserv-jbise.jar .


           
 
                                                                           
 Accessing the EJBs deployed on GlassFish from ServiceMix using Java EE Service Engine
           

A sample application demonstrates how to access EJB webservice deployed on GlassFish application server from ServiceMix using Java EE Service Engine.

           

This is the schema of flow of the sample.

           

Flow

           

The source code of this sample is available in GlassFish workspace under appserv-tests/devtests/webservice/jbi-serviceengine/sm/bc_consumer_se_provider
            The sample has all the necessary ant tasks predefined. All that is needed is to run $glassfish_dir/bin/asant command.

           

Here are the steps to checkout the GlassFish source code and run the sample :

           
# Checkout GlassFish

cvs -d :pserver:guest@cvs.dev.java.net:/cvs checkout glassfish

# Run the sample

cd glassfish/appserv-tests/devtests/webservice/jbi-serviceengine/sm/bc_consumer_se_provider $glassfish_dir/bin/asant
           
           

 

           
 
                                                                           
 More Info

                                                                                                                                                                                                                                                                                                                                                                                                                       
GlassFishhttp://glassfish.dev.java.net/
                        http://wiki.java.net/bin/view/Projects/GlassFish
                        http://glassfishwiki.org/jbiwiki/
Java EE Service Enginehttps://glassfish.dev.java.net/javaee5/jbi-se/ServiceEngine.html
                        http://download.java.net/general/open-esb/docs/jbi-components/jee-se.html
                        http://weblogs.java.net/blog/binod/archive/2006/07/java_ee_service_1.html
ServiceMixhttp://servicemix.org
OpenESBhttp://open-esb.dev.java.net/
           
 

 

 


My previous blog entry explained about AVK and how to use AVK from within NetBeans.

I thought it is worthwhile spending some time in writing down the detailed description of the tasks involved in writing this plug-in. This article is intended for the NetBeans plug-in developers.

 

Implementation details of AVK plug-in :

This is what the AVK plug-in does internally:

1. Adds "Dynamic Verification" action to "Verify Project" task.

2. When "Dynamic Verification" action is invoked then the action handler

    A. Configures application server to run in AVK mode (by invoking MBeans deployed on the application server through JMX APIs).
    B. Deploys the application (by invoking "run-deploy" ANT task)
    C. Lauches the application in the browser if the application has web component.
    D. Invokes the AVK tool (by creating a seperate process)
    E. Launches "AVK Session" window showing the dynamic verification results.

3. Source code linking from the swing UI : From the "AVK Session" window - Right click "Servlet Name" or "Method Name" > "Go To Source" to view the appropriate source code of your application.
In the following sections I will be describing each of the implementation details outlined above. I am explaining the implementation details in general not being specific to AVK plug-in.


    Step 0. Creating a new NetBeans plug-in project


File > New Project > NetBeans Plug-in Modules > Module Project (Choose Project name[=AVK plug-in] and code base name[=avkplugin] and leave the rest at default values).

    Step 1. Adding an action to the Projects menu

Project > Right Click > New > Action (Select Category=Tools, Menu=Tools, Position=Update Center - HERE, Classname=MyAction).

Open layer.xml and change

<folder name="Menu"> to <folder name="Projects">
<folder name="Tools"> to <folder name="Tools">

In my case, layer.xml looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE filesystem PUBLIC "-//NetBeans//DTD Filesystem 1.1//EN" "http://www.netbeans.org/dtds/filesystem-1_1.dtd">
<filesystem>
    <folder name="Actions">
        <folder name="Tools">
            <file name="avkplugin-MyAction.instance"/>
        </folder>
    </folder>
    <folder name="Projects">
        <folder name="Actions">
            <attr name="org-netbeans-modules-autoupdate-UpdateAction.instance/avkplugin-MyAction.shadow" boolvalue="true"/>
            <file name="avkplugin-MyAction.shadow">
                <attr name="originalFile" stringvalue="Actions/Tools/avkplugin-MyAction.instance"/>
            </file>
        </folder>
    </folder>
</filesystem>


Change MyAction.java to inherit from NodeAction instead of from CallableSystemAction.  Implement performAction(...) method. In my case I simply print 'My action is invoked' message. Here is my MyAction.java

package avkplugin;

import org.openide.nodes.Node;
import org.openide.nodes.NodeAcceptor;
import org.openide.util.HelpCtx;
import org.openide.util.NbBundle;
import org.openide.util.actions.NodeAction;

public final class MyAction extends NodeAction {
    
    public void performAction(Node[] nodes) {
        System.out.println("My action is invoked...");
    }
    
    public String getName() {
        return NbBundle.getMessage(MyAction.class, "CTL_MyAction");
    }
    
    protected void initialize() {
        super.initialize();
        // see org.openide.util.actions.SystemAction.iconResource() javadoc for more details
        putValue("noIconInMenu", Boolean.TRUE);
    }
    
    public HelpCtx getHelpCtx() {
        return HelpCtx.DEFAULT_HELP;
    }
    
    protected boolean asynchronous() {
        return false;
    }

    protected boolean enable(Node[] node) {
        return true;
    }
    
}

At this point, my project uses the following libraries:

Nodes APIs
Utilities APIs

To install the plug-in : Project > Right Click > Install/Reload in Development IDE.

After the plug-in is successfully installed "My Action" action gets added to the Project. To invoke the action : Project > Right Click > My Action. You will see "My action is invoked" on the console from where you launched the IDE.


    Step 2A: Invoking MBeans deployed on the application server using JMX APIs:

Let us take an example of reading the deployment directory of a JavaEE application from the domain.xml

package avkplugin;

import java.util.HashMap;
import java.util.Map;
import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import org.netbeans.api.project.Project;
import org.netbeans.modules.j2ee.deployment.devmodules.spi.J2eeModuleProvider;
import org.netbeans.modules.j2ee.deployment.plugins.api.InstanceProperties;
import org.openide.nodes.Node;
import org.openide.util.HelpCtx;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.openide.util.actions.NodeAction;

public final class MyAction extends NodeAction {
    
    public void performAction(Node[] nodes) {
        System.out.println("Deployment directory = " + getDeployDir(getProject(nodes[0])));
    }
    
    public Project getProject(Node projectNode) {
        Lookup lookup = projectNode.getLookup();
        Project project = (Project)lookup.lookup(Project.class);
        return project;
    }
    
    public String getDeployDir(Project project) {
        
        Lookup projectLookup = project.getLookup();
        J2eeModuleProvider moduleProvider = (J2eeModuleProvider)projectLookup.lookup(J2eeModuleProvider.class);
        
        String moduleName = moduleProvider.getDeploymentName().toLowerCase();
        String objectName = "com.sun.appserv:type=j2ee-application,name=" + moduleName + ",category=config";
        
        InstanceProperties instProps = moduleProvider.getInstanceProperties();
        
        String deployDir = null;
        
        String adminUser = instProps.getProperty("username");
        String adminPassword = instProps.getProperty("password");
        String adminPort = instProps.getProperty("httpportnumber");
        String jmxUrl = "service:jmx:rmi:///jndi/rmi://localhost:8686/jmxrmi"; // hardcoded.
        
        String[] credentials = new String[] { adminUser, adminPassword };
        Map env = new HashMap();
        env.put("jmx.remote.credentials", credentials);
        try {
            JMXServiceURL url = new JMXServiceURL(jmxUrl);
            JMXConnector jmxc = JMXConnectorFactory.connect(url, env);
            MBeanServerConnection mbsc = jmxc.getMBeanServerConnection();
            deployDir = (String)mbsc.getAttribute(new ObjectName(objectName), "location");
        } catch(Exception ex) {
            ex.printStackTrace();
        }
        return deployDir;
    }
    
    .....

}

At this point, my project uses the following additional libraries:
J2EE Server Registry
Project API


    Step 2B: Deploying the application by invoking "run-deploy" ANT task

This is how one can invoke any build target from within an action

.....
import org.apache.tools.ant.module.api.support.ActionUtils;
import org.netbeans.spi.project.support.ant.GeneratedFilesHelper;
import org.openide.filesystems.FileObject;
.....

public final class MyAction extends NodeAction {

    public void performAction(Node[] nodes) {
        runBuildTarget(getProject(nodes[0]), "run-deploy");
    }

    .....

    public void runBuildTarget(Project project, String targetName) {
        FileObject buildXML = project.getProjectDirectory().getFileObject(GeneratedFilesHelper.BUILD_XML_PATH);
        try {
            ActionUtils.runTarget(buildXML, new String[]{targetName}, new Properties());
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    .....
}

Additional libraries needed for this purpose

File System API
Ant
Ant-Based Project Support
Execution API


    Step 2C: Launching the application in the browser

This is how one can launch the application in the browser

.....
import org.openide.awt.HtmlBrowser.URLDisplayer;
.....

public final class MyAction extends NodeAction {

    .....

    public void launchApp(Project project) {
        Lookup projectLookup = project.getLookup();
        J2eeModuleProvider moduleProvider = (J2eeModuleProvider)projectLookup.lookup(J2eeModuleProvider.class);
        String contextRoot = moduleProvider.getConfigSupport().getWebContextRoot();
        String httpPort = "8080"; // hardcoded. Correct way of getting it is : mbsc.getAttribute(new javax.management.ObjectName("com.sun.appserv:type=http-listener,id=http-listener-1,config=server-config,category=config") ,"port");
        String url = "http://localhost:" + httpPort + "/" + contextRoot;
        try {
            URLDisplayer.getDefault().showURL(new URL(url));
        } catch (MalformedURLException ex) {
            ex.printStackTrace();
        }
    }
    .....
}


Additional libraries needed for this purpose

UI Utilities API


    Step 2D: Invoking a tool by creating a new process

This is pretty simple. We can just use the standard JDK Runtime APIs.

        String commandString = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java" + " -classpath " + "<classpath>" + " avkplugin.Tool";
        Process p = Runtime.getRuntime().exec(commandString);
        p.waitFor();


    Step 2E: Lauching a window in the editor pane

This can be done using the swing UI builder capabilities of the NetBeans IDE.


    Step 3: Source code linking

If className, methodName and argument types are known then we can open the source file and take the cursor to the exact method. This is how we can achieve that:

.....
import org.netbeans.jmi.javamodel.Element;
import org.netbeans.jmi.javamodel.JavaClass;
import org.netbeans.jmi.javamodel.Method;
import org.netbeans.jmi.javamodel.Resource;
import org.netbeans.jmi.javamodel.Type;
import org.netbeans.modules.java.JavaEditor;
import org.netbeans.modules.javacore.api.JavaModel;
import org.netbeans.modules.javacore.internalapi.JavaMetamodel;
import org.openide.loaders.DataObject;
import org.openide.text.PositionBounds;
.....

public final class MyAction extends NodeAction {
    .....

    private List getMethodParams(String[] paramTypes) {
        List methodParams = new ArrayList();
        for(int i=0; i<paramTypes.length; i++) {
            String param = paramTypes[i];
            Type type = JavaModel.getDefaultExtent().getType().resolve(param);
            methodParams.add(type);
        }
        return methodParams;
    }

    public void openSource(String className, String methodName, String[] methodParamTypes) {
        List methodParams = getMethodParams(methodParamTypes);
        JavaClass javaClass = (JavaClass)JavaModel.getDefaultExtent().getType().resolve(className);
        FileObject fo = JavaModel.getFileObject(javaClass.getResource());
        try {
            if(methodName != null) {
                Resource resource = JavaModel.getResource(fo);
                JavaMetamodel javaMetamodel = JavaMetamodel.getManager();
                Method m = javaClass.getMethod(methodName,methodParams,true);
                Element element = resource.getElementByOffset(m.getStartOffset());
                PositionBounds position = javaMetamodel.getElementPosition(element);
                ((JavaEditor) javaMetamodel.getDataObject(element.getResource()).getCookie(JavaEditor.class)).openAt(position.getBegin());
            } else {
                DataObject dobj = DataObject.find(fo);
                OpenCookie oc = (OpenCookie)dobj.getCookie(OpenCookie.class);
                oc.open();
            }
        } catch (Throwable t) {
            StatusDisplayer.getDefault().setStatusText("Unable to open " + className);
            t.printStackTrace();
        }
    }

    .....
}

Additional libraries needed for this purpose

JMI for Java Language Model
Java Language Model Implementation
JMI Reflective API
Text API
Java Source Files
Datasystems API
Window System API
JMI Utilities

Note :  Add <compile-dependency/> for <code-name-base>org.netbeans.modules.jmiutils</code-name-base>


Conclusion :

I hope the NetBeans plug-in developers find some of the tips and the utility methods described above useful. Please provide me your comments/feebback.




Introduction:


In my recent project I had to develop a NetBeans plug-in for AVK. The name of the plug-in is "Sun Java System Application Verification Kit". This plug-in is available at the NetBeans Development Update Center.

This blog is intended for the NetBeans users who want to use AVK through NetBeans IDE.

In this blog I brief about the AVK tool and the plug-in functionalities. Here is the flashplayer demo which has the whole process installing the plug-in and doing the dynamic verification of a JavaEE project using this AVK plug-in.


AVK Tool:

AVK tool helps application developers to test their applications for correct use of Java EE APIs and portability across Java EE compatible application servers. The tool determines that an application suite follows the Java EE platform specification (including all the specifications for the technologies that are part of the platform) and that it runs on the Application Server. There are two stages of verification:
       
  • Static verification : Statically checking the application for the correct usage of Java EE specifications. The tool determines that an application suite's classes, Java EE annotations, and any deployment descriptors follow the specification and that the application suite contains no methods that are proprietary to a particular vendor.
  •    
  • Dynamic verification : Dynamically checking the application for the correct usage of Java EE specification by running the application on the Sun Java System Application Server. The tool profiles the application, determining the proportion of enterprise bean component methods, web services methods, and web components that are invoked while the application runs on the application server.
More details about the AVK can be found at http://java.sun.com/j2ee/avk


Plug-in functionalities:

       
  • Static verification plug-in : This is the plug-in for doing the static verification of the JavaEE project through NetBeans IDE. Since the static verifier a subcomponent of the application server itself, the plug-in for the static verifier is available as part of "Sun Java System Application Server plug-in".
To do static verification of the JavaEE project - Right click the project > Select "Verify Project" > Select "Static Verification" from the dialog box. The static verification results will be displayed in the output panel.
       
  • Dynamic verification plug-in : This is the plug-in for doing the dynamic verification of the JavaEE project through NetBeans IDE. This plug-in is called "Sun Java System Application Verification Kit plug-in".
To do dynamic verification of the JavaEE project :
       
  1. In the Projects window, select the project node, right-click and choose Verify Project. The Choose Verification dialog box opens.
  2.    
  3. Select the Dynamic Verification radio button and click OK. The IDE deploys your project to the application server, starts the verification process, and brings up 'AVK Session' window in the editor pane showing the dynamic verification reports. The reports in this window get automatically updated as you access your application.
  4.    
  5. If your project has a web component then your application is launched in the browser. Otherwise, execute a sample manually outside the IDE.
  6.    
  7. You will notice that the reports are automatically updated in 'AVK Session' window. Note that 'Right Click' > 'Go To Source' on any 'Servlet Name' or 'Method Name' will open the appropriate source code. 'Right Click > 'Show Exception' on any exception of the unsuccessfully called Servet/EJB will show the exception stack trace on the output panel.
Screen shot of the 'AVK Session' window showing the dynamic verification results for a stateless EJB sample :

avk-blog-1.png

Note that the plug-in also supports the J2EE projects running with AppServer 8.x as runtime. Open "Help > Help Contents" and search "AVK" for more details.


Conclusion :

In this blog I gave a brief introduction to the AVK tool and explained how to use NetBeans AVK plug-in. I hope NetBeans and AVK users find this blog useful. Please provide me your comments/feebback.

Implementation details of the AVK plug-in will be available in my next blog. That will be intended more towards the NetBeans plug-in developers.




Technorati Tags: glassfish netbeans