Select to view content in your preferred language

GeoEvent Server: Forward Proxy on a Custom HTTP Transport

3644
0
06-18-2019 08:52 AM
EricIronside
Esri Regular Contributor
2 0 3,644

One of the most common requests for the HTTP Transport is to implement some custom authentication steps that are required by an external API.  Unfortunately, it is impossible to implement the HTTP Transport in a way that can accommodate all of the possible permutations.  So it becomes necessary to write your own HTTP Transport to include the desired authentication functionality. But how can you do this while still incorporating the default Proxy capabilities provided by the GeoEvent Server?  This blog will show you how to access the underlying proxy properties and implement your HTTP Transport so that you don't have to re-invent the proxy capability from scratch.

NOTE: This blog post assumes you are already familiar with developing custom transports for GeoEvent. If not, please take a look at the GeoEvent SDK documentation provided with your GeoEvent Server installation at:

<GeoEventServerInstallLocation>\ArcGIS\Server\GeoEvent\sdk\GeoEvent Developer Guide.pdf

System Proxy Settings  

GeoEvent Server provides global settings for the proxy on both HTTP and HTTPS schemes.  To access these settings, open GeoEvent Manager, navigate to Site > Settings and scroll down to the Http Proxy Settings and/or Https Proxy Settings sections.  Here you can set the name of the proxy host, the port it is listening on, and the credentials to use. Not all proxy configurations will utilize all of these settings. If you set a host name without specifying a port the system will utilize the default port number for the schema requested (80 for HTTP and 443 for HTTPS).  If you don't specify a username/password, then the proxy request won't include those credentials in the request (the proxy is open from the inside).

One typical implementation includes a forward proxy listening on a single port (default to port 80) that will forward both HTTP and HTTPS schemes. In this case, the settings for HTTP and HTTPS would be the same:

  • Http Proxy Host: myhost.company.com
  • Https Proxy Host: myhost.company.com
  • Https Proxy Port: 80

Once you've set up your proxy settings, you should be able to test them using a standard input that utilizes the HTTP transport (like the Poll and ArcGIS Server for Features that is requesting data from ArcGIS Online).  

Configure A Custom Transport Service

The first thing you need to do to create a custom transport service that will be able to take advantage of the underlying system's proxy support is to get access to the GeoEvent HTTP Client Service.  The blueprint config.xml file should look something like the following.  The important parts are 1) adding the reference to the blueprint to the GeoEventHttpClientService and then 2) add that reference to the service bean as a property (OSGI will inject the GeoEventHttpClientService into the service bean once it is created).

<?xml

            version="1.0"

            encoding="UTF-8"?>
<blueprint

            xmlns="http ://www.osgi.org/xmlns/blueprint /v1.0.0"

            xmlns:ext="http ://aries.apache.org/blueprint/xmlns/blueprint-ext/v1.0.0">
            <reference

                        id="geoEventHttpClientService"

                        interface="com.esri.ges.core.http.GeoEventHttpClientService" />
            <bean

                        id="myTransportServiceBean"

                        class="com.esri.geoevent.transport.custom.MyTransportService"

                        activation="eager">
                        <property

                                    name="bundleContext"

                                    ref="blueprintBundleContext" />
                        <property

                                    name="geoEventHttpClientService"

                                    ref="geoEventHttpClientService" />
            </bean>
            <service

                        id="geotabTransportService"

                        ref="geotabTransportServiceBean"

                        interface="com.esri.ges.transport.TransportService" />
</blueprint>

In the service java code, add a setGeoEventHttpClientService method to allow the injection of the GeoEvent Http Client Service. Then pass that GeoEvent Http Client Service to your transport when it is created.

private GeoEventHttpClientService httpClientService;

public void setGeoEventHttpClientService(GeoEventHttpClientService httpClientService) {
    this.httpClientService = httpClientService;
}

@Override
public Transport createTransport() throws ComponentException {
    return new MyTransport(definition, httpClientService);
}

This GeoEvent Http Client Service will be able to create Http Clients that implement the underlying proxy capabilities of GeoEvent. If you use the Http Clients the service creates, you can rest assured that the Global GeoEvent Settings for proxy values will be honored.

 

Create the GeoEvent Http Client

In your transport implementation there are a few things to note:

  1. Every time your transport starts, you should create a new HttpClient.
  2. Every time your transport stops, you should close your current HttpClient.

Whenever the properties of your connection are changed, the transport is stopped then started again, so if you follow these rules, you will be guaranteed to use a HttpClient with the correct settings.

In the start() method of your transport, you should create a new HttpClient using the GeoEventHttpClientService. This will create a GeoEventHttpClient that is able to properly proxy your requests.In the stop() method, you will want to close the the HttpClient to free up the resources. Please note that I've left out some try/catch/finally calls here for clarity.  

import com.esri.ges.core.http.GeoEventHttpClient;

...

public class MyTransport extends InboundTransportBase {

private GeoEventHttpClient httpclient;

...

@Override

public synchronized void start() {

    ...

    this.httpclient = httpClientService.createNewClient();

    ...

}

...

@Override
public synchronized void stop() {

    ...

    this.httpclient.close();  // try/catch around this!

    this.httpclient = null;

    ...

}

Creating Requests Using the HttpClient

There are a number of methods on the GeoEvent Http Client that will allow you to create proxy requests. Please note that you must use one of these methods to create your request in order for it to properly utilize the proxy.  

createGetRequest(URL url, Collection<KeyValue> parameters)

This method creates a GET request with the provided list of parameters appended to the request as URL Parameters. The KeyValue Collection parameters can be null, this results in no URL Parameters at the end of the request URL.

Example

URL:             https ://my.org.com/APICall 

Parameters: ({key1,value1},{key2,value2})

Result:          GET https ://my.org.com/APICall?key1=value1&key2=value2

createGetRequest(URL url, String acceptableTypes)

This method creates a GET request with the provided acceptable types set in the header properties. The String acceptableTypes can be null, in that case the header values will not be set.

Example

URL:             https ://my.org.com/APICall 

acceptableTypes: application/json

Result:          GET https ://my.org.com/APICall [content-type=application/json, accept=application/json]

createPostRequest(URL url, String postBody, String contentType)

This method creates a POST request with the provided string body of content type. 

Example

URL:             https ://my.org.com/APICall 

postBody: postBody

acceptableTypes: application/json

Result:          POST https ://my.org.com/APICall BODY=StringEntity(content-type=application/json, entity="postBody")

createPostRequest(URL url, Collection<KeyValue> parameters)

This method creates a POST request with the provided list of parameters embedded in the post body as a URL Encoded Form. The KeyValue Collection parameters can be null, this will result in an empty URL Encoded From Entity.

Example

URL:             https ://my.org.com/APICall 

Parameters: ({key1,value1},{key2,value2})

Result:          POST https ://my.org.com/APICall  [content-type=application/x-www-form-urlencoded,

                     charset=utf-8BODY=UrlEncodedFormEntity(parameters, "UTF-8")

Using the Proxy Http Request

Once the Http Request object is created, you can modify the properties or the entity as needed. For example, if you need a JSON entity inside of a URL Encoded Form request:

    // default content type is "application/x-www-form-urlencoded"
    HttpPost httpPost = httpclient.createPostRequest(url, null);

    // If your request entity is "application/json"
    String requestData = "JSON-RPC=" + URLEncoder.encode(requestString, "utf-8");
    StringEntity entity = new StringEntity(requestData, jsonContentType);
    httpPost.setEntity(entity);

To execute the request via the proxy, use the GeoEventHttpClient from above. 

    try (CloseableHttpResponse response = httpclient.execute(httpPost))
    {

        ...  // Do stuff with the response

    } catch (Exception e) {

        ...

    }

About the Author
Esri Professional Services Real-Time GIS Team GeoEvent Sr. Product Enginner