Create a Java Callback Listener in NetBeans

Overview

For Wireless Network Services API service requests that require significant processing time, such as provisioning service for a device or changing a service plan, the ThingSpace Platform replies first with a synchronous response that acknowledges the request and allows your application to continue processing. Later, the platform sends an asynchronous callback message, reporting the results of the request.

The synchronous response contains a unique Request ID. The callback message contains the same Request ID so that you can associate the callback contents with the original API request.

ThingSpace Platform callback services provide your application with real-time updates about service changes. They also create less load on your systems because your application does not have to poll for updates, which means better performance.

To receive callback messages, you must create and deploy one or more web services that can validate and process SOAP messages that conform to the M2M CallbackData schema. You must register the URLs of your web services with the ThingSpace Platform so that it knows where to send the callback messages.

Routing, Firewall, and Security Considerations

Your company must host your callback listening web services at an IP address or URL that can be reached by the ThingSpace Platform callback servers. (This means that you probably cannot use your own computer to test callbacks, unless you have an externally visible IP address that leads to your computer.)

For security reasons, your IT department will probably put the hosting servers behind your company firewall. The firewall must be configured to allow HTTP requests from the Verizon M2M servers to reach the servers hosting your listening service. Contact your Verizon Wireless business sales representative or sales engineer for the IP addresses used by the Verizon M2M callback services.

Requests from the M2M callback services will use TLS 1.1 or 1.2 protocols to secure the messages. Other protocols are not supported.

Create a Simple Callback Listener Service

This tutorial walks you through the steps to create a simple callback listening service that validates incoming requests and returns a callback response that contains the request ID. This simple service does not do anything with the callback data, but there are comments in the code showing where you could act on the data.

Software Required

Create a Project

  1. From the File menu, select New Project .
  2. Select the Java Web category, then Web Application project, and then click Next .

  3. Enter a name for the project, and then click Next . This tutorial uses "cbListener."

  4. Select the server (GlassFish Server 4) and Java EE Version (Java EE 7 Web), and set the Context Path to where the service will be accessed on the server.

  5. Click Finish .
  6. After NetBeans creates the project, you can close the editing window for index.html. You won't need that file for the callback listening service.

Create a Web Service Template from the WSDL and XSD Files

These steps will create a simple web service stub from the callback WSDL and XSD files.

  1. Right-click on your project and select New > Web Service from WSDL .

  2. Type a Web Service Name . This tutorial uses "listener."

  3. Enter the name of the Package that you want to contain your web service class. This tutorial uses com.yourcompany.m2mlistener. The wizard will also create classes from the WSDL, and will automatically put them in a package named com.nphase.unifiedwebservice.v2 because that name is defined in the WSDL file.
  4. Select the CallbackService_WNS_Gen2.wsdl.
  5. Tick the box to Implement Web service as Stateless Session Bean .
  6. Click Finish .

    The wizard converts the WSDL to Java classes under the Generated Sources (jax-ws) virtual folder It also creates and opens the listener.java file (or whatever name you used for the web service) that contains callback and smsCallback method stubs.

  7. Right-click on your project and select New > JAXB Binding (or New > Other > XML > JAXB Binding ).

  8. Enter a Binding Name . This tutorial uses "CallbackDataSchema."
  9. Click Browse to navigate to the CallbackData_WNS_Gen2.xsd schema file.
  10. Enter com.nphase.unifiedwebservice.v2.data as the Package Name .

  11. Click Finish .

    The wizard converts the XML schema definitions to Java classes under the Generated Sources (JAXB) virtual folder.

Implement the CallbackService

The callback method in the listener class accepts a CallbackRequest object, which is a SOAP message that came in as an HTTP request from the ThingSpace Platform and has been converted to a Java object. It returns a CallbackResponse object, which gets converted to XML and is sent back to the ThingSpace Platform as an HTTP response.

  1. Add import statements for CallbackRequest, CallbackResponse, and CallbackData classes. Note that the CallbackData class is in the com.nphase.unifiedwebservice.v2. data class.

    package com.yourcompany.m2mlistener
    
    import javax.ejb.Stateless;
    import javax.jws.WebService;
    
    import com.nphase.unifiedwebservice.v2.CallbackRequest;
    import com.nphase.unifiedwebservice.v2.CallbackResponse;
    import com.nphase.unifiedwebservice.v2.data.CallbackData;
    
  2. Add import statements for these other classes that you'll need as you implement the service:

    import java.io.StringReader;
    import java.io.StringWriter;
    import javax.xml.bind.JAXBContext;
    import javax.xml.bind.JAXBElement;
    import javax.xml.bind.JAXBException;
    import javax.xml.bind.Unmarshaller;
    import javax.xml.transform.Transformer;
    import javax.xml.transform.TransformerFactory;
    import javax.xml.transform.TransformerException;
    import javax.xml.transform.dom.DOMSource;
    import javax.xml.transform.stream.StreamResult;
    import org.w3c.dom.Node;
    
  3. Edit the callback method in the listener class to follow this logic, or copy and paste this code. The highlighted lines are placeholders for code that would do something with the callback data, but that is beyond the scope of this tutorial; you'll have to comment them out to be able to compile and run without errors.

    @WebService(
          serviceName = "CallbackService", 
          portName = "CallbackService", 
          endpointInterface = "com.nphase.unifiedwebservice.v2.CallbackService", 
          targetNamespace = "http://nphase.com/unifiedwebservice/v2", 
          wsdlLocation = "WEB-INF/wsdl/listener/CallbackService_WNS_Gen2.wsdl")
    @Stateless
    public class listener {
    
       public CallbackResponse callback(CallbackRequest callbackRequest)  throws TransformerException {
            
          // If the request is not null, record the request ID
          // then verify that the Data object is not null
          if (callbackRequest == null) {
             throw new NullPointerException("Null request");
          } else {
             System.out.println("Request ID: " + callbackRequest.getRequestId());
          }
          if (callbackRequest.getData() == null) {
             throw new NullPointerException("Null data");
          }
            
          // The Data object needs to be cast to a Node object
          // and then converted to a string of XML
          String cbDataXml = new String();
          Node node = (Node) callbackRequest.getData().getAny();
          cbDataXml = getOuterXml(node);
            
          // Use JAXB to unmarshal the XML string into a CallbackData object
          try {
             JAXBContext jc = JAXBContext.newInstance(CallbackData.class);
             Unmarshaller u = jc.createUnmarshaller();
             StringReader reader = new StringReader(cbDataXml);
             CallbackData cbData = (CallbackData) ((JAXBElement) u.unmarshal(reader)).getValue();
                
             // If the request doesn't contain a SOAP fault, you can do whatever you need to with the contents.
             // This code shows how you could verify that the callback is for a ChangeDeviceState-Activate request, 
             // then compares the request ID with one that was stored from the response to an Activate request, 
             // and then adds the device identifiers to a database.
             if (cbData.getFault().isNil()) {
                if (!cbData.getCarrierService().isNil() &&
                       !cbData.getCarrierService().getValue().getChangeDeviceState().isNil() &&
                       !cbData.getCarrierService().getValue().getChangeDeviceState().getValue().getActivate().isNil()) {
                   // Check if the request ID matches the one for the device that you activated
                   if (callbackRequest.getRequestId() == activateRequestId) {
                      // Loop through the device identifiers
                      // and call a method to store them somewhere
                      for (com.nphase.unifiedwebservice.v2.data.DeviceIdentifier deviceId :
       cbData.getCarrierService().getValue().getChangeDeviceState().getValue().getActivate().getValue().getDeviceIdentifierCollection()) {
                         AddToActivatedDevices(deviceId.getKind(), deviceId.getIdentifier());
                         // For this tutorial, print the device identifier info
                         System.out.println(deviceId.getKind() + " = " + deviceId.getIdentifier());
                      }
                   }
                }
             } else {
                 // callbackRequest contained a SOAP fault
                 System.out.println("Fault error: " + cbData.getFault().getValue().getFaultcode().toString()
                    + "\n" + cbData.getFault().getValue().getFaultstring());
             }
          } catch (JAXBException e) {
             System.out.println("Unmarshalled object is not of type CallbackData");
             System.out.println(e.getMessage());
          }
            
          //Return a CallbackResponse that contains the request ID to acknowledge 
          // having received the callback request
          CallbackResponse cr = new CallbackResponse();
          cr.setRequestId(callbackRequest.getRequestId());
          return cr;
       }
    
  4. Create the getOuterXml() method.

        // Get the markup containing this node and all its child nodes.
        public static String getOuterXml(Node node) throws TransformerException {
            Transformer transformer = TransformerFactory.newInstance().newTransformer();
            transformer.setOutputProperty("omit-xml-declaration", "yes");
            StringWriter writer = new StringWriter();
            transformer.transform(new DOMSource(node), new StreamResult(writer));
            return writer.toString();
        }
    
  5. The wizard that consumed the WSDL created a placeholder for the smsCallback method in your class. If you are registering to receive SMS callbacks, you can start with this simple code.

        public com.nphase.unifiedwebservice.v2.SmsCallbackResponse smsCallback(com.nphase.unifiedwebservice.v2.SmsCallbackRequest smsCallbackRequest) {
                System.out.println("Received SMS Message: " + smsCallbackRequest.getSmsMessage());
                if (!smsCallbackRequest.getDevice().isEmpty()) {
                    for (com.nphase.unifiedwebservice.v2.DeviceIdentifier deviceId : smsCallbackRequest.getDevice()) {
                        System.out.println("From device " + deviceId.getKind() + " = " + deviceId.getIdentifier());
                    }
                }
    
                return new com.nphase.unifiedwebservice.v2.SmsCallbackResponse();
        }
    

Register to Receive Callback Messages

After you have built your callback listener web service and have it running on a server, you need to register the URL of the web service with the ThingSpace Platform so that it knows where to send the callback messages. You only need to do this once for each type of callback message (see the note below); the ThingSpace Platform will store the setting and always send callback messages for your account to that URL. The UWS Explorer tool (included in the Tools and Examples folder in the SDK) is an easy way to register your URL, or you can use a SOAP client like SoapUI, as described here. However, if you choose to do it programmatically, the steps are described below.

NOTE: The ThingSpace Platform can send several different types of callback messages, each relating to a different type of information. This tutorial only describes CarrierService callback messages for device activations. You do not need to subscribe to all callback messages -- only to those that are applicable to your needs.

  1. Add the import statements required to work with callback registration, and those required to add the session token to request headers.

    import org.apache.axiom.soap.impl.dom.soap11.SOAP11Factory;
    import org.apache.axiom.om.OMElement;
    
    import com.nphase.unifiedwebservice.v2.RegisterCallback;
    import com.nphase.unifiedwebservice.v2.CallbackRegistrationServiceStub;
    import com.nphase.unifiedwebservice.v2.RegisterCallbackResponse;
    import org.datacontract.schemas._2004._07.nphase_unifiedwebservice_apis_v2_contract_callbackregistrationservice.RegisterCallbackRequest;
    
  2. Add a method to register a URL as a callback listener for a specific service.

    NOTE: There are several services for which you can register a callback. See the Callback Registration Service page for a description of these services. This example use the CarrierService because it sends responses for device activations.

       private static void Registercallback(String service, String callbackUrl) {
    
          // Create a request for registering the callback and set the
          // account name, service name and URL for your listening service.
          RegisterCallbackRequest request = new RegisterCallbackRequest();
          request.setAccountName("MyAccount");
          request.setServiceName(service);
          request.setUrl(callbackUrl);
    
          RegisterCallback rcbObj = new RegisterCallback();
          rcbObj.setInput(request);
    
          try {
             // Create a new CallbackRegistrationService object
             CallbackRegistrationServiceStub callbackRegSvcStub = new CallbackRegistrationServiceStub();
    
    
             // Follow these steps to add the session token to a request header
             // Create an Axis2 soap factory to generate SOAP elements
             SOAP11Factory factory = new SOAP11Factory();
             // Then use the factory to create a "token" element
             OMElement e = factory.createOMElement("token", factory.createOMNamespace("http://nphase.com/unifiedwebservice/v2", "v2"));
             // Set the text of the token element to the current sessionToken 
             e.setText(sessionToken);
             // Add the element to the CallbackRegistrationService object
             callbackRegSvcStub._getServiceClient().addHeader(e);
    
             // Send the request
             RegisterCallbackResponse response = stub.registerCallback(rcbObj);
    
             //For the tutorial, display a success message
             System.out.println("Callback listener registered for " + response.getOutput().getServiceName());
    
          } catch (AxisFault e1) {
             // ...
          } catch (RemoteException e) {
             // ...
          }
       }
    
  3. Call the new method from main() and pass in a service name and URL.

       public static void main(String[] args) {
          Wns session = new Wns()
          session.Login("username", "password");
          session.Registercallback("CarrierService", "http://www.example.com/callbacklistener");
       }
    

    NOTE: Allowed port numbers for new callback registrations are 80, 443, 9001-9006 and 50551-50559.