Handle SOAP requests and responses in SOIs

To handle SOAP requests in an SOI, you must implement IRequestHandler and IRequestHandler2. These interfaces contain two methods, handleBinaryRequest() and handleStringRequest(), which allow you to intercept SOAP requests and execute your own business logic.

The handleBinaryRequest() method handles all the SOAP binary requests that come from ArcObjects-based clients, such as ArcMap, ArcGIS Engine, or custom ArcObjects applications. For example, accessing and rendering a map service on ArcMap makes the requests come through the handleBinaryRequest() method.

The handleStringRequest() method handles all the SOAP XML string requests that come from non-ArcObjects-based clients, such as requests made via SOAP SDK.

To preprocess the SOAP request, you must unpack the incoming request, customize the request parameters, repack the request, and send it to the underlying service object for processing. Similar procedure also applies to postprocessing responses. To postprocess the SOAP response, you must unpack the response, customize the response content, repack the response to appropriate format, and send it back to the client. The SDK contains utility classes to assist with packing and unpacking the relevant request and response objects.

This topic demonstrates the workflows for handling SOAP binary requests using the handleBinaryRequest() method and SOAP string requests using the handleStringRequest() method.

Use the handleBinaryRequest() method

The following code in the handleBinaryRequest() method is automatically generated if you create an SOI from soi-archetype.

        
1
2
3
4
5
6
7
8
    public byte[] public byte[] handleBinaryRequest(byte[] request) throws IOException, AutomationException {
        serverLog.addMessage(3, 200, "Request received in Sample Object Interceptor for handleBinaryRequest");
        IRequestHandler requestHandler = soiHelper.findRequestHandlerDelegate(so);
        if (requestHandler != null) {
            return requestHandler.handleBinaryRequest(capabilities, request);
        }
        return null;
    }

Once a SOAP binary request is received, the above handleBinaryRequest() function will be triggered, and its request variable will be populated with the request information, such as request name and request parameters. Based on these variables, the IRequestHandler.handleBinaryRequest() method will generate the response, which is also a byte array.

The following code snippet demonstrates how to preprocess a SOAP binary request. It keeps only the first layer visible for the export operation of a map service, no matter how many layers exist in the map service.

To achieve this function, you must find the parameter that controls the layer visibility for the export operation. Based on SOAP SDK, the SOAP method for the export operation is ExportMapImage, whose input parameter MapDescription defines the content of the map to be exported. One of MapDescription's properties is LayerDescriptions, which is an array of LayerDescription with the setVisible() method to control the layer visibility. In this way, you can track down the objects to customize in the SOI.

Sample code:

                        
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
    public byte[] handleBinaryRequest(byte[] request) throws IOException, AutomationException {

        IRequestHandler requestHandler = soiHelper.findRequestHandlerDelegate(so);
        if (requestHandler != null) {

            IMessage requestMessage = SOIHelper.convertBinaryRequestToMessage(request);
            String requestName = requestMessage.getName();
            serverLog.addMessage(3, 200, "Request name is " + requestName); //about server logs, please refer to topic: how to audit requests in SOIs
            if ("ExportMapImage".equals(requestName)) {
                IXMLSerializeData requestParams = requestMessage.getParameters();
                int mapDescriptionIndex = requestParams.find("MapDescription");
                IMapDescription mapDescription = (IMapDescription)requestParams.getObject(mapDescriptionIndex, requestMessage.getNamespaceURI(), "MapDescription");
                int layerCount = mapDescription.getLayerDescriptions().getCount();
                for (int i= layerCount-1;i>0;i--) {
                    mapDescription.getLayerDescriptions().getElement(i).setVisible(false);
                }
                byte[] modifiedRequest = SOIHelper.convertMessageToBinaryRequest(requestMessage);
                return requestHandler.handleBinaryRequest(modifiedRequest);
            }

            return requestHandler.handleBinaryRequest(request);
        }
        return null;
    }

The above code first calls the SoapSOIHelper.convertBinaryRequestToMessage() method to convert the byte-array request to IMessage, which provides access to the request name string and request parameters as IXMLSerializeData. Second, the IXMLSerializeData.getObject() method is called to retrieve IMapDescription, which is the input parameter to customize in this workflow. Then, ILayerDescription can be returned from the IMapDescription.getLayerDescriptions array and ILayerDescription.setVisible() can be called. Last, the SOIHelper.convertMessageToBinaryRequest() method converts the modified request back to byte-array format. This modified byte-array request can then be passed to IRequestHandler.handleBinaryRequest() for the service object to process and generate the corresponding response based on the modified request.

Postprocessing SOAP binary responses works in a similar way. First, you must obtain the byte-array response returned from the IRequestHandler.handleBinaryRequest() method with either the original or modified request. Next, you will convert the byte-array response to IMessage using the SOIHelper.convertBinaryRequestToMessage() method. The IMessage interface provides access to the response content via IMessage.getParameters(), which returns an object that can be cast to the IXMLSerializeData interface. Calling the IXMLSerializeData.getObject() method will return the Result object. The Result object can be cast to the corresponding interface or class which provides access to the element for modification. After the response element is modified, you must convert it from IMessage to a byte array by using SOIHelper.convertMessageToBinaryRequest() and return the byte-array response to the client.

To test the above code, you can deploy the SOI and enable the SOI with a map service. After you add the map service to ArcMap, only the first layer should display on the map. As this code only modifies ExportMapImage, to customize other SOAP methods, such as GetLegendInfo, you can refer to the Java layer access SOI sample.

Use the handleStringRequest() method

The following code in the handleStringRequest() method is automatically generated if you create an SOI from soi-archetype.

           
1
2
3
4
5
6
7
8
9
10
11
    public String handleStringRequest(String capabilities, String request) throws IOException, AutomationException {
        // Log message with server
        serverLog.addMessage(3, 200, "Request received in Sample Object Interceptor for handleStringRequest");
         // Find the correct delegate to forward the request too
        IRequestHandler requestHandler = soiHelper.findRequestHandlerDelegate(so);
        if (requestHandler != null) {
            // Return the response
            return requestHandler.handleStringRequest(capabilities, request);
        }
        return null;
    }

Unlike the handleBinaryRequest() method, handleStringRequest() intercepts all the SOAP requests that come from non-ArcObjects-based clients, such as XML requests sent to the service SOAP endpoint from custom applications using SOAP SDK.

When the request reaches the service SOAP endpoint, the handleStringRequest() method is triggered and the requests are directly passed in as the request string variable. Based on the request and capabilities variables, IRequestHandler.handleStringRequest() generates the response, which is also an XML string.

To preprocess the SOAP string request, you will first convert the request string to IMessage. Then, the request parameters can be accessed from the IMessage.getParameters() method and cast to IXMLSerializeData, whose getObject() method can be used to retrieve the underlying request object for modification. After the request object is modified, you must call the SOIHelper.createNewIMessage() method to copy a new IMessage from the original one, and use the SOIHelper.addObjectToXMLSerializeData() method to commit the changes of the request object to the copied IMessage, which can then be converted back to a string via SOIHelper.convertMessageToStringRequest(). This string can be passed to IRequestHandler.handleStringRequest() for the service object to process and generate a response.

The same logic also applies to postprocessing SOAP string responses. In the following sample code, the SOI adds a custom message to the Comments property of the map service’s GetDocumentInfo response.

To customize this response, you must understand the response object type first. Based on SOAP SDK, the GetDocumentInfo method returns a PropertySet which contains a PropertySetProperty named Comments. This PropertySetProperty can be accessed via IPropertySet in Enterprise SDK and its value can be set by the IPropertySet.setProperty() method. Note that the modified PropertySet must be committed to IMessage copied from the SOIHelper.createNewIMessage() method.

Sample code:

                        
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
    public String handleStringRequest(String capabilities, String request)
            throws IOException, AutomationException {
        // Find the correct delegate to forward the request too
        IRequestHandler requestHandler = soiHelper.findRequestHandlerDelegate(so);
        if (requestHandler != null) {
            String response = requestHandler.handleStringRequest(capabilities, request);
            IMessage responseMessage = SOIHelper.convertStringRequestToMessage(response);
            if ("GetDocumentInfoResponse".equals(responseMessage.getName())) {
                IXMLSerializeData orginalParams = responseMessage.getParameters();
                int resultObjIndex = orginalParams.find("Result");
                IPropertySet propSet = (IPropertySet)orginalParams.getObject(resultObjIndex,
                        responseMessage.getNamespaceURI(), "PropertySet");
                propSet.setProperty("Comments", "Custom message from SOI.");
                IMessage modifiedResponseMsg = soiHelper.createNewIMessage(responseMessage);
                IXMLSerializeData modifiedData = modifiedResponseMsg.getParameters();
                soiHelper.addObjectToXMLSerializeData("Result", propSet, "PropertySet", modifiedData);
                String modifiedResponse = SOIHelper.convertMessageToStringRequest(modifiedResponseMsg);
                return modifiedResponse;
            }
            // Return the response
            return requestHandler.handleStringRequest(capabilities, request);
        }
        return null;
    }

In the above code, the request variable is directly passed to the IRequestHandler.handleStringRequest() method to generate the raw response string. Then, by calling SOIHelper.convertStringRequestToMessage(), the raw response string is converted to IMessage, which provides access to the response name and the response content as IXMLSerializeData.

As this code intercepts the response from the GetDocumentInfo method, the name of the response should be GetDocumentInfoResponse. Typically, the name of the response is the service’s SOAP method name with Response appended, and the name of the SOAP response content is Result. In this case, the Result object is of type PropertySet (see GetDocumentInfo). You can set the value of the Comments property by using IPropertySet.setProperty().

Next, you must call the SOIHelper.CreateNewIMessage() method to copy a new response IMessage from the original IMessage. This will allow the updated response element IPropertySet to be committed to the new IMessage via the the SOIHelper.AddObjectToXMLSerializeData() method.

Finally, you can convert the modified response IMessage to a string through the SOIHelper.convertMessageToStringRequest() method. This modified string response will be returned in the handleStringRequest() method and sent to the client.

To test how the above code works in the SOI, you can deploy the SOI and enable the SOI with a map service. You can use any non-ArcObjects-based SOAP client to send a GetDocumentInfo request, and you will find the value of the Comments property in the response XML shows the custom message defined in the SOI.

Your browser is no longer supported. Please upgrade your browser for the best experience. See our browser deprecation post for more details.

;