Clarity

 View Only

Consuming PPM web services - Apache Axis2 (Java) 

Jul 07, 2017 10:34 PM

Objectives

  1. Download Apache Axis2
  2. Generate stub classes using Apache Axis2's wsdl2java emitter
  3. Create classes to consume the web service
  4. Create a project in Eclipse that can be built and compiled
  5. Export to a .jar file that you can call from the command line to run.
  6. Call a PPM NSQL query through the web service and iterate through the record results
  7. View the raw query response, to enable further manipulation of the document using XSLT or other tools

 

 

Assumptions

Before we begin:

  1. This is part of a series of articles, but it is not the first.
  2. The first article covers Apache Axis 1.4 and can be found here: Consuming PPM web services - Apache Axis (Java)
  3. This article will cover things that are different, so consider the first article a pre-requisite to this one, including its own assumptions.

 

 

Download Apache Axis2

If you view the current (CA PPM 15.2) release notes, it will say that the supported Third Party SOAP Integration Toolkit is Apache Axis 1.6 (source).

 

From 1.5 and up, strictly speaking, it is Apache Axis2 and the highest version of Apache Axis created was 1.4.

 

In this article we will be using Apache Axis2 1.7, which is to all intents and purposes identical to use as version 1.6.  At this time, it can be downloaded from the following location until it is archived and superseded by a newer release.  You should download the binary release:

Apache Axis2 - Releases

 

 

 

Generate stub classes using Apache Axis2's wsdl2java emitter

The syntax for Apache Axis2 has changed, and the instructions currently in the CA PPM 15.2 documentation will not work with it.  Additionally, the ADB databindings (Axis' default) are stricter now, and the current WSDL being emitted by CA PPM will not work with it as it is and it needs to be changed.

 

The following steps will cover what needs to be done:

  1. Navigate in your browser to your CA PPM server's /niku/wsdl/Query url
  2. Locate the name of the query you want to consume as a service in the list, and click on the (save as) link for it.  After downloading, I like to set the file extension to .xsl because your browser may save it without any.
  3. Edit the file in your preferred text editor and makes the following changes:
    1. In the namespace declarations at the top, add a new entry with this text:
      xmlns:xog="http://www.niku.com/xog"
    2. Just inside the <types> element, add a new <schema> with the following 3 lines of content:
      <xsd:schema elementFormDefault="qualified" targetNamespace="http://www.niku.com/xog">
          <xsd:element name="SessionID" type="xsd:string"/>
      </xsd:schema>
    3. Locate the <message> for LoginResult and change the element attribute value from
      tns:SessionID
      to
      xog:SessionID
  4. Now save the file.

 

Here all the changes and additions have been highlighted:

 

If they are not already set, you will need to set two environment variables before using the stub generator.  Adjust the paths accordingly to where you have installed the JDK and extracted the Axis2 release to.  Mine are as follows:

java_home=c:\Java\jdk1.8.0_31

axis2_home=c:\ProjectX\axis2-1.7.5

 

Now we can run the generator.  Note that this time, the output path does not end in \src and that is deliberate, this version of the emitter will create that folder anyway.  This is run from the command prompt in the folder where Axis2 was extracted.  Note that the parameter is now -uri and not -W and that we are using the locally saved .xsl file (change the path to reach it as needed).

bin\wsdl2java.bat -o c:\ProjectX\check_nils2 -uri check_nillable.xsl

 

Example output:

 

And here is the list of files and folders created - a more succinct list than with Apache Axis prior to 1.5.

C:\ProjectX\check_nils2>dir /a-d /s /b
C:\ProjectX\check_nils2\build.xml
C:\ProjectX\check_nils2\src\com\niku\www\xog\query\Check_nillableQueryServiceCallbackHandler.java
C:\ProjectX\check_nils2\src\com\niku\www\xog\query\Check_nillableQueryServiceStub.java

 

C:\ProjectX\check_nils2>

 

 

Create classes to consume the web service

Compared to previous, we have 2 classes and files to add.  The first is very similar to that used for CheckNils.java in the first article, but there are differences if you compare them that come as a necessity of the differences between Axis and Axis2.  The second class file is to introduce a new way to scoop up the raw SOAP messages being returned when you consume the web service, acting as a handler that will be called automatically by Axis2 whenever there is a response received.

 

First create the folder structure we want for this demo from your 'c:\projectx\check_nils2' folder this time.  The package name in our class has the same structure, and is the same as before:

C:\ProjectX\check_nils2>md src\com\ca\ppm\demo

 

C:\ProjectX\check_nils2>

 

Now create the file CheckNils.java in the newly created folder using the following code, remember that it is different from the first article's class content.

 

CheckNils.java

package com.ca.ppm.demo;

import java.rmi.RemoteException;

import org.apache.axis2.AxisFault;

import com.niku.www.xog.query.Check_nillableQueryServiceStub;
import com.niku.www.xog.query.Check_nillableQueryServiceStub.*;

public class CheckNils {

     private static String username;
     private static String password;
     private static String url;
     private static String sessionId = null;
     private static Check_nillableQueryServiceStub service;
     private static QueryResult result;
     private static Auth auth;
     private static Logout logout;

     public static void main(String[] args) throws RemoteException {

          ParseArgs(args);
          CreateServicePort();
          Login();
          if (sessionId != null) {
               RunQuery();
               ShowResults();
               Logout();
          }

     }

     private static void Logout() throws RemoteException {
          service.logout(logout);
     }

     private static void ShowResults() {
          for (Check_nillableRecord record : result.getQueryResult().getRecords().getRecord())
               System.out.println("Resource: " + record.getRsrc() + " Manager: " + record.getManager() + " CheckMe: "
                         + record.getCheckme());
     }

     private static void RunQuery() throws RemoteException {
          Query query = new Query();
          query.setQuery(new Check_nillableQuery());
          result = service.query(query, auth);
     }

     private static void Login() throws RemoteException {
          Login login = new Login();
          login.setUsername(username);
          login.setPassword(password);

          try {
               SessionID session = service.login(login);
               if (session != null)
                    sessionId = session.getSessionID();
          } catch (AxisFault e) {
               System.out.println("\nLogin failed.  Check username and/or password and try again.\n");
          }

          if (sessionId != null) {
               System.out.println("Logged in as " + username + ":" + sessionId);

               auth = new Auth();
               auth.setSessionID(sessionId);

               logout = new Logout();
               logout.setSessionID(sessionId);
          }
     }

     private static void CreateServicePort() throws AxisFault {
          service = new Check_nillableQueryServiceStub(url + "/niku/xog");
          service._getServiceClient().getAxisService().addMessageContextListener(new MessageHandler());
     }

     private static void ParseArgs(String[] args) {
          boolean showUsage = true;

          if (args.length == 1) {
               String param = args[0];
               int slashPosition = param.indexOf("/");

               if (slashPosition > 0) {
                    int atPosition = param.indexOf("@", slashPosition);

                    if (atPosition > 0 && atPosition < param.length()) {
                         username = param.substring(0, slashPosition);
                         password = param.substring(slashPosition + 1, atPosition);
                         url = param.substring(atPosition + 1);
                         showUsage = false;
                    }
               }
          }

          if (showUsage)
               ShowUsage();
     }

     private static void ShowUsage() {
          String fqname = CheckNils.class.getName();
          System.out.println("Usage: " + fqname + " username/password@url");
          System.exit(0);
     }
}

 

 

And here is the code for MessageHandler.java in the same folder.

 

MessageHandler.java

package com.ca.ppm.demo;

import org.apache.axis2.context.MessageContext;
import org.apache.axis2.context.ServiceContext;
import org.apache.axis2.description.MessageContextListener;

public class MessageHandler implements MessageContextListener {

     @Override
     public void attachEnvelopeEvent(MessageContext arg0) {
          if(arg0 != null)
          {
               System.out.println("\nEnvelopeEvent: \n" + arg0.getEnvelope().toString());
          }
         
     }

     @Override
     public void attachServiceContextEvent(ServiceContext arg0, MessageContext arg1) {
          // Not used
     }

}

 

 

Create a project in Eclipse that can be built and compiled

Follow the same steps again from the first article, with the following differences:

  1. The project name and folder is now check_nils2 and not check_nils
  2. Add the external jars that come with your extracted Apache Axis2's \lib folder.  There are a lot more of them this time, but you can add them all in one go by selecting all in the dialog.

 

 

 

 

Export to a .jar file that you can call from the command line to run

There are no changes here - follow everything as per the first article.

 

 

Call a PPM NSQL query through the web service and iterate through the record results

There are no changes here either - follow the first article, but compare minor differences in the outputs, namely with respect to the raw SOAP responses.

 

C:\ProjectX\check_nils2\bin>java -classpath \ProjectX\axis2-1.7.5\lib\*;. com.ca.ppm.demo.CheckNils admin/admin@http://claritynam17
log4j:WARN No appenders could be found for logger (org.apache.axiom.locator.DefaultOMMetaFactoryLocator).
log4j:WARN Please initialize the log4j system properly.

 

EnvelopeEvent:
<?xml version='1.0' encoding='utf-8'?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <soapenv:Header/>
  <soapenv:Body>
    <SessionID xmlns="http://www.niku.com/xog">5363086__20F7543D-1318-4A03-AF54-0119BC2B86F1</SessionID>
  </soapenv:Body>
</soapenv:Envelope>

Logged in as admin:5363086__20F7543D-1318-4A03-AF54-0119BC2B86F1

 

EnvelopeEvent:
<?xml version='1.0' encoding='utf-8'?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <soapenv:Header/>
  <soapenv:Body>
    <QueryResult xmlns="http://www.niku.com/xog/Query">
      <Code>check_nillable</Code>
      <Records>
        <Record>
          <rsrc>Administrator, PPM</rsrc>
          <manager>Administrator, PPM</manager>
          <checkme xsi:nil="true"/>
          <project_count>16</project_count>
        </Record>
      </Records>
      <Slice>
        <Number>0</Number>
        <Size>1</Size>
        <Total>1</Total>
      </Slice>
    </QueryResult>
  </soapenv:Body>
</soapenv:Envelope>
Resource: Administrator, PPM Manager: Administrator, PPM CheckMe: null

 

C:\ProjectX\check_nils2\bin>

 

View the raw query response, to enable further manipulation of the document using XSLT or other tools

As we are using a message context handler to intercept the SOAP responses and read the raw data, and so the output above is a little different to how it was in the Apache Axis 1.4 demo.  As such, the previous ShowRaw() method has been removed and I would draw your attention instead now to the MessageHandler class implementing the MessageContextListener interface, and how it is hooked up in CheckNils.CreateServicePort() method.

Statistics
0 Favorited
2 Views
0 Files
0 Shares
0 Downloads

Tags and Keywords

Comments

Oct 12, 2017 11:09 AM

When working around the ADB data bindings error above with Object based requests (e.g. Projects, Resources, AllObjects, etc.) instead of using the Query service, it is almost the same.  Just instead of locating the LoginResult message in order to change it from tns:SessionID to xog:SessionID, it is called the LoginResponse message, so change that latter one.

 

This workaround should not be needed with PPM 15.3 and above (for either Object or Query requests).

Oct 12, 2017 11:04 AM

My apologies for not seeing this sooner, the notification was lost in a period that I was out of office for an extended duration.

 

The 50,000 records limit (at once) is imposed by a system governor.  Your portlet will be working fine because it is performing pagination of the results - showing you maybe only 20-50 records at once.

 

Reference: TEC1677811 Governor Limits in CA PPM

 

In order for web services to 'play nice' in the PPM environment, they too need to work by pulling results of the same query in pages at a time.  You can use higher page sizes than 20-50, even up to thousands in some cases, but all web services that are working with fetching the results of our NSQL queries should make sure they use the pagination features.

 

The minimal code above doesn't include that, as there were already a lot of steps and actions to undertake for people new to the field (at least from the PPM business rules standpoint), but you can see in the results it generates that it is still trying to give paging feedback to help guide you in the Slice element:

 

...

      <Slice>
        <Number>0</Number>
        <Size>1</Size>
        <Total>1</Total>
      </Slice>

...

 

If I can revisit with an updated version for handling that, I will try.  Some related information about these features of the Query web services (in general terms, not Axis specific) is here: https://communities.ca.com/thread/99505645

 

As you'll see, this Slice element is something you can both set and read from on your Query class requests in order to handle the paging and related (e.g. sorting) features.

Aug 11, 2017 02:58 AM

Hello Nick,

 

Your post has been very helpful to me, I used it to get query results from a CA PPM query and them generate a file with these results. My query retrieves a large number or records (> 1.000.000) , but when I call it from the java program I only get 50.000, do you know if there is a limit in the numbers of records that can be retrieved using the web service?. I can get all the results using a portlet in CA PPM, so the query looks like is working correctly.

 

Thanks very much in advance

 

Best Regards

Pilar

Related Entries and Links

No Related Resource entered.