Seapine Labs
Personal tools

SDK NetBeans Tutorial

From Seapine Labs

Jump to: navigation, search

Works with TestTrack 2008.1

Works with NetBeans 6.1

If you would like to take advantage of the TestTrack SOAP SOAP based SDK, you have at your disposal several options as to which language to use. Using Java can be tricky since you first must find an IDE that allows you to work with web services and can build Java classes from the WSDL file.


While Axis is a very powerful and popular tool, there are some parsing issues with Axis version 1.4 that can make it very frustrating and as of the time of this writing, the latest version of Axis2 is not able to create the Java classes from TestTrack WSDL.


A relatively easy way to create SOAP applications using Java is with NetBeans. The following is a tutorial that will take you step by step on how to create a simple Java application that you can use to get a list of defects, updates a defect, among other actions.


NOTE: This is not a tutorial on how to use NetBeans or write Java applications. The sole purpose of this article is to show how to use the TestTrack SOAP based SDK in NetBeans. You may find a different and maybe better way to use NetBeans for developing SOAP applications.

This is meant as an example only and Seapine does not offer support for this.

Contents

[edit] Don't want to use NetBeans?

If you do not want to work with NetBeans, you can just download the Generated Java Classes. Simply import the needed Jar files into your project using your favorite IDE.

[edit] The environment

The following example uses TestTrack 2008.1.2 and NetBeans IDE 6.1 (Build 200805300101). Older versions of TestTrack or NetBeans should also work.

Screenshots included in this article are of NetBeans or TestTrack in a Windows environment.

[edit] Creating the project

Creating the project is very straight forward. In NetBeans, select File > New Project.... The New Project dialog appears. The New Project dialog consists of two sections:


1. Choose Project - Under Categories select Java and under Projects: select Java Application.


Click on Next. This takes you to the second section.


2. Name and Location - Under Project Name: enter the name of the project. In this example we will use the name "ttSoapSample". You can leave the location as is or you can change it to a different location. Leave the box checked next to Create Main Class. Optionally you can leave the Set as Main Project box checked. This really should only be left checked if this is the project you are going to be spending most of your time.


Figure 1 - New project dialog
Figure 1 - New project dialog

[edit] Setting up the web service interface

The next major step is to step up an interface with the TestTrack SOAP server. NetBeans can create a web service client based on a WSDL file. While this works for the most part, there are a couple of things you must do to get it to work properly.

NOTE: You can always just download the Generated Java Classes.

[edit] Handling a known issue

The main issue is with the Web Service Client in NetBeans not handling dashes in the WSDL properly and creating variables in Java with those dashes in the name which causes compile errors.


Other Java utilities like Axis remove the dashes when declaring the Java variables.


These variables are used mainly when creating a defect object. They are used both for the list of source code files (CSCCFileRecord) and file attachments (CFileAttachment).

Here is a list of these variables:

CSCCFileRecord

m-strFileName

m-strFixedRevision

m-dateFixedTimestamp

m-strType

m-scriptOrder

m-pFileData


CFileAttachment


m-strFileName

m-strArchiveName

m-strType

m-scriptOrder

m-strScriptState



Keep in mind that the dashes need only to be handled while processing Java code. When the XML file is built, those variables will need to have dashes in them, as defined in the WSDL file.


This leaves you with two options:


1. Edit the WSDL file and remove the dashes for those variables, and then edit the XML serializer classes to include the dashes when building the XML files.


2. Create the classes using the standard WSDL, let NetBeans complain about the syntax issues and manually fix the variable names. There are about 100 edits required and you may have to do this every time the client refreshes itself.

By far the easiest is to go with option #1 and that is what will be done in this example.


NOTE: This is only required if you need to build a defect, test case or test run object. If your application only needs to retrieve a list of defects, for example, you do not need to worry about editing the SOAP serializer classes.

[edit] Editing the WSDL file

NOTE: Make a backup copy of the ttsoapcgi.wsdl file before making any changes.

Open the ttsoapcgi.wsdl file with your favorite XML editor. Search for any "-" characters and either replace them with an underscore or just remove them from the variables listed above.

The first section you will see these variables in is:

<complexType name="CSCCFileRecord">
   <complexContent>
    <extension base="ttns:CItemWithDBRecordId">
     <sequence>
     <element name="m-strFileName" type="xsd:string" minOccurs="1" maxOccurs="1" nillable="false"/>
     <element name="m-strFixedRevision" type="xsd:string" minOccurs="0" maxOccurs="1" nillable="true"/>
     <element name="m-dateFixedTimestamp" type="xsd:dateTime" minOccurs="0" maxOccurs="1" nillable="true"/>
     <element name="m-strType" type="xsd:string" minOccurs="0" maxOccurs="1" nillable="true"/>
     <element name="m-scriptOrder" type="xsd:long" minOccurs="0" maxOccurs="1"/>
     <element name="m-strScriptState" type="xsd:string" minOccurs="0" maxOccurs="1" nillable="true"/>
     </sequence>
    </extension>
   </complexContent>
  </complexType>


After removing the dashes , it now looks like this:

<complexType name="CSCCFileRecord">
   <complexContent>
    <extension base="ttns:CItemWithDBRecordId">
     <sequence>
     <element name="mstrFileName" type="xsd:string" minOccurs="1" maxOccurs="1" nillable="false"/>
     <element name="mstrFixedRevision" type="xsd:string" minOccurs="0" maxOccurs="1" nillable="true"/>
     <element name="mdateFixedTimestamp" type="xsd:dateTime" minOccurs="0" maxOccurs="1" nillable="true"/>
     <element name="mstrType" type="xsd:string" minOccurs="0" maxOccurs="1" nillable="true"/>
     <element name="mscriptOrder" type="xsd:long" minOccurs="0" maxOccurs="1"/>
     <element name="mstrScriptState" type="xsd:string" minOccurs="0" maxOccurs="1" nillable="true"/>
     </sequence>
    </extension>
   </complexContent>
  </complexType>

The next section where you will find the variables is here:

<complexType name="CFileAttachment">
   <sequence>
     <element name="m-pFileData" type="xsd:base64Binary" minOccurs="0" maxOccurs="1" nillable="true"/>
     <element name="m-strFileName" type="xsd:string" minOccurs="0" maxOccurs="1" nillable="true"/>
     <element name="m-strArchiveName" type="xsd:string" minOccurs="0" maxOccurs="1" nillable="true"/>
     <element name="m-strType" type="xsd:string" minOccurs="0" maxOccurs="1" nillable="true"/>
     <element name="m-scriptOrder" type="xsd:long" minOccurs="0" maxOccurs="1"/>
     <element name="m-strScriptState" type="xsd:string" minOccurs="0" maxOccurs="1" nillable="true"/>
   </sequence>
  </complexType>

After doing the same with these variables, the section now looks like this:

</complexType>
  <complexType name="CFileAttachment">
   <sequence>
     <element name="mpFileData" type="xsd:base64Binary" minOccurs="0" maxOccurs="1" nillable="true"/>
     <element name="mstrFileName" type="xsd:string" minOccurs="0" maxOccurs="1" nillable="true"/>
     <element name="mstrArchiveName" type="xsd:string" minOccurs="0" maxOccurs="1" nillable="true"/>
     <element name="mstrType" type="xsd:string" minOccurs="0" maxOccurs="1" nillable="true"/>
     <element name="mscriptOrder" type="xsd:long" minOccurs="0" maxOccurs="1"/>
     <element name="mstrScriptState" type="xsd:string" minOccurs="0" maxOccurs="1" nillable="true"/>
   </sequence>
  </complexType>

[edit] Handling a requirement

The WSDL is rpc encoded and you will need to download the JAX-RPC Web Service plug-in for NetBeans.

[edit] Creating the web service client

Since we are going to need to edit some of the java files, we need to include them in the project. It is recommended that you create a separate package to hold these files.

In this example, the package is named "testtrack_Interface" under the Source Packages folder.

While the project is selected, select File > New File. The New File dialog appears. Under Categories select Web Services and then under File Types select Web Service Client.


Click on Next.


The WSDL and Client Location appears. Choose the location of the WSDL file. If the WSDL file is located on your machine, you may select "Local File" and browse for the file. If the file is on a web server, choose the WSDL URL option.


In this example, the ttsoapcgi.wsdl is installed on the C:\inetpub\wwwroot\ folder on the same machine. Therefore either Local File: (C:\inetput\wwwroot\ttsoapcgi.wsdl) or WSDL URL (http://localhost/ttsoapcgi.wsdl) options are valid.


Next, select "JAX-RPC Style" from the Client Side drop down. This selection will in turn enable you to select the package to include the client in. Select the package you created to store the web service client files.


Click on Finish


Figure 2 -New Web Service Client dialog
Figure 2 -New Web Service Client dialog


In the output pane you will see messages about the compile process. You may see warnings about WS-I non comforming operations. At the end you should see BUILD SUCCESSFUL.

Figure 3 -Build output
Figure 3 -Build output

[edit] Getting the files in the package

You will notice that if you now go into the testtrack_Interface package, it is still empty, eventhough you specified to place the files in there and also the ttsoapcgi-config.xml file in the META-INF.wsdl package also references the testtrack_Interface package.


You must manually copy the files into this package.


Browse to the folder where your project is stored and find the \build\generated\wsclient directory. There you will see a folder with the pacakage name. Copy the folder and replace the empty folder with the same name under the \src directory.

The package in NetBeans now shows all of the Java classes created by the Web Service Client.


[edit] Editing the classes

As mentioned earlier, the last thing we need to do is edit two files:

CSCCFileRecord_SOAPSerializer.java


CFileAttachment_SOAPSerializer.java

In each file you will find a section where it builds the section of the XML file for the file attachments and source code files.


The portion of the CFileAttachment_SOAPSerializer file looks like this:


public class CFileAttachment_SOAPSerializer extends ObjectSerializerBase implements Initializable {
    private static final javax.xml.namespace.QName ns1_m_pFileData_QNAME = new QName("", "mpFileData");
    private static final javax.xml.namespace.QName ns3_base64Binary_TYPE_QNAME = SchemaConstants.QNAME_TYPE_BASE64_BINARY;
    private CombinedSerializer ns3_myns3_base64Binary__byte_Base64Binary_Serializer;
    private static final javax.xml.namespace.QName ns1_m_strFileName_QNAME = new QName("", "mstrFileName");
    private static final javax.xml.namespace.QName ns3_string_TYPE_QNAME = SchemaConstants.QNAME_TYPE_STRING;
    private CombinedSerializer ns3_myns3_string__java_lang_String_String_Serializer;
    private static final javax.xml.namespace.QName ns1_m_strArchiveName_QNAME = new QName("", "mstrArchiveName");
    private static final javax.xml.namespace.QName ns1_m_strType_QNAME = new QName("", "mstrType");
    private static final javax.xml.namespace.QName ns1_m_scriptOrder_QNAME = new QName("", "mscriptOrder");
    private static final javax.xml.namespace.QName ns3_long_TYPE_QNAME = SchemaConstants.QNAME_TYPE_LONG;
    private CombinedSerializer ns3_myns3__long__java_lang_Long_Long_Serializer;
    private static final javax.xml.namespace.QName ns1_m_strScriptState_QNAME = new QName("", "mstrScriptState");


After making the changes, it looks like this:


public class CFileAttachment_SOAPSerializer extends ObjectSerializerBase implements Initializable {
    private static final javax.xml.namespace.QName ns1_m_pFileData_QNAME = new QName("", "m-pFileData");
    private static final javax.xml.namespace.QName ns3_base64Binary_TYPE_QNAME = SchemaConstants.QNAME_TYPE_BASE64_BINARY;
    private CombinedSerializer ns3_myns3_base64Binary__byte_Base64Binary_Serializer;
    private static final javax.xml.namespace.QName ns1_m_strFileName_QNAME = new QName("", "m-strFileName");
    private static final javax.xml.namespace.QName ns3_string_TYPE_QNAME = SchemaConstants.QNAME_TYPE_STRING;
    private CombinedSerializer ns3_myns3_string__java_lang_String_String_Serializer;
    private static final javax.xml.namespace.QName ns1_m_strArchiveName_QNAME = new QName("", "m-strArchiveName");
    private static final javax.xml.namespace.QName ns1_m_strType_QNAME = new QName("", "m-strType");
    private static final javax.xml.namespace.QName ns1_m_scriptOrder_QNAME = new QName("", "m-scriptOrder");
    private static final javax.xml.namespace.QName ns3_long_TYPE_QNAME = SchemaConstants.QNAME_TYPE_LONG;
    private CombinedSerializer ns3_myns3__long__java_lang_Long_Long_Serializer;
    private static final javax.xml.namespace.QName ns1_m_strScriptState_QNAME = new QName("", "m-strScriptState");


A similar section is found in the CSCCFileRecord_SOAPSerializer file:


public class CSCCFileRecord_SOAPSerializer extends ObjectSerializerBase implements Initializable {
    private static final javax.xml.namespace.QName ns1_recordid_QNAME = new QName("", "recordid");
    private static final javax.xml.namespace.QName ns3_long_TYPE_QNAME = SchemaConstants.QNAME_TYPE_LONG;
    private CombinedSerializer ns3_myns3__long__long_Long_Serializer;
    private static final javax.xml.namespace.QName ns1_m_strFileName_QNAME = new QName("", "mstrFileName");
    private static final javax.xml.namespace.QName ns3_string_TYPE_QNAME = SchemaConstants.QNAME_TYPE_STRING;
    private CombinedSerializer ns3_myns3_string__java_lang_String_String_Serializer;
    private static final javax.xml.namespace.QName ns1_m_strFixedRevision_QNAME = new QName("", "mstrFixedRevision");
    private static final javax.xml.namespace.QName ns1_m_dateFixedTimestamp_QNAME = new QName("", "mdateFixedTimestamp");
    private static final javax.xml.namespace.QName ns3_dateTime_TYPE_QNAME = SchemaConstants.QNAME_TYPE_DATE_TIME;
    private CombinedSerializer ns3_myns3_dateTime__java_util_Calendar_DateTimeCalendar_Serializer;
    private static final javax.xml.namespace.QName ns1_m_strType_QNAME = new QName("", "mstrType");
    private static final javax.xml.namespace.QName ns1_m_scriptOrder_QNAME = new QName("", "mscriptOrder");
    private static final javax.xml.namespace.QName ns1_m_strScriptState_QNAME = new QName("", "mstrScriptState");

After making the changes, the same section now looks like:

public class CSCCFileRecord_SOAPSerializer extends ObjectSerializerBase implements Initializable {
    private static final javax.xml.namespace.QName ns1_recordid_QNAME = new QName("", "recordid");
    private static final javax.xml.namespace.QName ns3_long_TYPE_QNAME = SchemaConstants.QNAME_TYPE_LONG;
    private CombinedSerializer ns3_myns3__long__long_Long_Serializer;
    private static final javax.xml.namespace.QName ns1_m_strFileName_QNAME = new QName("", "m-strFileName");
    private static final javax.xml.namespace.QName ns3_string_TYPE_QNAME = SchemaConstants.QNAME_TYPE_STRING;
    private CombinedSerializer ns3_myns3_string__java_lang_String_String_Serializer;
    private static final javax.xml.namespace.QName ns1_m_strFixedRevision_QNAME = new QName("", "m-strFixedRevision");
    private static final javax.xml.namespace.QName ns1_m_dateFixedTimestamp_QNAME = new QName("", "m-dateFixedTimestamp");
    private static final javax.xml.namespace.QName ns3_dateTime_TYPE_QNAME = SchemaConstants.QNAME_TYPE_DATE_TIME;
    private CombinedSerializer ns3_myns3_dateTime__java_util_Calendar_DateTimeCalendar_Serializer;
    private static final javax.xml.namespace.QName ns1_m_strType_QNAME = new QName("", "m-strType");
    private static final javax.xml.namespace.QName ns1_m_scriptOrder_QNAME = new QName("", "m-scriptOrder");
    private static final javax.xml.namespace.QName ns1_m_strScriptState_QNAME = new QName("", "m-strScriptState");

Now you are ready to code!

[edit] Writing Code

Now the real fun begins, we are ready to actually start writing code.

[edit] Invoking a web service operation

NetBeans offers a really neat tool that can help you get started. You can call a web service operation and have NetBeans place actual code in your java class. It is not complete code (it does not fill out paramters, for example), but for your first time it can be of great help. Once you become more familiar with the TestTrack SOAP interface you may find yourself using this less and less.


On the Main.java file, place your cursor under the "// TODO code application logic " comments, right click and select Web Service Client Resources > Call Web Service Operation. The Select Operation to Invoke dialog appears.

Figure 4 -  Select Operation to Invoke dialog
Figure 4 - Select Operation to Invoke dialog

For this example, we'll select the getProjectList and click OK. The following code now appears in the Main.java file:

try { // This code block invokes the Ttsoapcgi:getProjectList operation on web service
	testtrack_Interface.Ttsoapcgi ttsoapcgi = new testtrack_Interface.Ttsoapcgi_Impl();
	testtrack_Interface.TtsoapcgiPortType _ttsoapcgi = ttsoapcgi.getTtsoapcgi();
	_ttsoapcgi.getProjectList(/* TODO enter operation arguments*/);
} catch(javax.xml.rpc.ServiceException ex) {
	java.util.logging.Logger.getLogger(testtrack_Interface.Ttsoapcgi.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch(java.rmi.RemoteException ex) {
	java.util.logging.Logger.getLogger(testtrack_Interface.Ttsoapcgi.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch(Exception ex) {
	java.util.logging.Logger.getLogger(testtrack_Interface.Ttsoapcgi.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
}

All you have to do now is fill out the parameters where the comments /* TODO enter operation arguments*/ appear. However, after you get the list of projects you may want to do something with them, so other code may also be necsessary.


The following is the same piece of code but with the following additions:


- Imports the testtrack_Interface package (to avoid having to prepend each object with the package name) and removes 'testtrack_Interface' from object definintions.

- Passes username and password to the getProjecList method.

- Loops through each project and prints out each name.

package ttsoapsample;

import testtrack_Interface.*;

/**
 *
 * @author cremerf
 */
public class Main {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        // TODO code application logic 
        
       try
       { // This code block invokes the Ttsoapcgi:getProjectList operation on web service
	   Ttsoapcgi ttsoapcgi = new Ttsoapcgi_Impl();
	   TtsoapcgiPortType _ttsoapcgi = ttsoapcgi.getTtsoapcgi();
           CProject[] projectList = _ttsoapcgi.getProjectList("Administrator","password");
           for (int i =0; i < projectList.length;i++)
           {
               System.out.println(projectList[i].getDatabase().getName());
        
            }
       }
       catch(javax.xml.rpc.ServiceException ex)
       {
	   java.util.logging.Logger.getLogger(testtrack_Interface.Ttsoapcgi.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
       }
       catch(java.rmi.RemoteException ex)
       {
	   java.util.logging.Logger.getLogger(testtrack_Interface.Ttsoapcgi.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
       }
       catch(Exception ex) 
       {
	   java.util.logging.Logger.getLogger(testtrack_Interface.Ttsoapcgi.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
       }

	
    }    

}

[edit] Setting the ttsoapcgi url

In the methods shown above the URL to the SOAP cgi is already set. The URL was obtained from the WSDL file and the URL in the WSDL file was set during the installaion of the SOAP components.

To change the URL programatically, invoke the ttsoapcgi object and change the URL as follows:


    //Define soap cgi object
    TtsoapcgiPortType_Stub _ttsoapcgi = (TtsoapcgiPortType_Stub)(new Ttsoapcgi_Impl().getTtsoapcgi());
    //Set the soap cgi URL
    _ttsoapcgi._setProperty(javax.xml.rpc.Stub.ENDPOINT_ADDRESS_PROPERTY, "http://yourserver/ttsoapcgi.exe");

[edit] Sample Methods

The following are excerpts from a class created to handle all calls made to the web service.

The TtsoapcgiPortType object (_ttsoapcgi) is generated when the class is instantiated and therefore can be reused each time the same instance of the _ttsoapcgi object is used.

NOTE: The following variables are declared as public and therefore are accessible to all of the methods:

ttCookie(long)

ttUsername(String)

ttPassword(String)

ttProjectName(String)

_ttsoapcgi(TtsoapcgiPortType)

[edit] Logging into a project

Logging into a project requires a CProject object. You can either get the list of projects and go through the array until you find the one you want to log into, or you can create a CProject object from scratch.

The example above shows you how to loop through a CProject[] array already, so the method below shows how to build a CProject object from scratch and then log in. If the process fails it returns false.


public boolean Login()
    {
        boolean result = false;
        //First we need to build the project object
        //Create the database object and set its name
        CDatabase ttDatabase = new CDatabase();
        ttDatabase.setName(ttProjectName);
        //Create the project options object
        CProjectDataOption[] ttProjOps = new CProjectDataOption[2];
        //Allow TestTrack Pro access
        ttProjOps[0].setName("TestTrack Pro");
        //Allow TestTrack TCM access
        ttProjOps[1].setName("TestTrack TCM");
        //Now create the project object
        CProject ttProj = new CProject();
        ttProj.setDatabase(ttDatabase);
        ttProj.setOptions(ttProjOps);
        
        //Now we can try to log in
        
        try
        { // This code block invokes the Ttsoapcgi:projectLogon operation on web service
	    
	    ttCookie = _ttsoapcgi.projectLogon(ttProj, ttUsername, ttPassword);
            if (ttCookie > 0)
            {
                result = true; 
            }
         }
         
         catch(Exception ex) 
         {
	     java.util.logging.Logger.getLogger(testtrack_Interface.Ttsoapcgi.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
         }

         return result;
        
    }

[edit] Getting a defect list array (CRecordListSoap)

This method can be used get a defects list. You must pass it a string array containing the columns to retrieve. The column names are those that are displayed in the defect list window.

public CRecordListSoap getDefects(String[] m_ColList)
    {
            //We do not want to get every single column, so we first must build our CTableColumn[] object
            //We initialize the size of the array to 3 since that is how many columns we are interested in
            CTableColumn[] m_Columns = new CTableColumn[m_ColList.length];
            
            for (int i = 0; i < m_ColList.length; i++)
            {
                //We need to instantiate each column (you'll get a null exception if you don't)
                m_Columns[i] = new CTableColumn();
                //After you instantiate the column, then give it the name
                m_Columns[i].setName(m_ColList[i]);
            }
            //Define our variable that will hold the defects
            //I am defining it here because I will use it outside of the 
            //embedded try catch below
            CRecordListSoap m_Records = new CRecordListSoap();
              
            try
            {
                //Get defect table name
                CDatabaseTable[] m_Tables = _ttsoapcgi.getTableList(ttCookie);
                //Populate the crl variable
                m_Records = _ttsoapcgi.getRecordListForTable(ttCookie, m_Tables[0].getName(), "",m_Columns  ); 
         
            }
            catch (Exception fcc)
            {
                java.util.logging.Logger.getLogger(Ttsoapcgi.class.getName()).log(java.util.logging.Level.SEVERE, null, fcc);
            }
        
            return m_Records;
    
    }

[edit] Get defect for edit

public CDefect GetDefectForEdit(long defNum)
    {
        CDefect m_Defect = new CDefect();
        try
        {        
            m_Defect = _ttsoapcgi.editDefect(ttCookie,defNum,"",false);
        }
        catch(java.rmi.RemoteException ex)
        {
	    java.util.logging.Logger.getLogger(Ttsoapcgi.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        return m_Defect;
        
    }

[edit] Save Defect


public boolean SaveDefect(CDefect m_Defect)
    {
        boolean result = false;
        int s_result = -1;
        try
        {
            s_result = _ttsoapcgi.saveDefect(ttCookie, m_Defect);
        }
        catch(java.rmi.RemoteException ex)
        {
	    java.util.logging.Logger.getLogger(Ttsoapcgi.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        if (s_result == 0)
        {
            result = true;
        
        }
        return result;
        
    }

[edit] Download

I have posted below a project I created following the steps in this article. It includes everything, including the .class files created by NetBeans. Note that this example does not employ the method that allows you to change the URL prgramatically.

It does contain the .java and .class files created from the WSDL.

NetBeans Project














Issue Management Software | Source Code Control Software | Test Case Management