Working with the ArcGIS Server ArcObjects API  

Extending server objects



An alternative to creating utility COM objects for use by server applications is to actually extend a server object with custom functionality. The goal of creating a server object extension is ultimately the same as extending the GIS server as previously described: to provide coarse-grained methods that do a lot of work in the server, rather than pay the cost of making a large number of calls into the server from the client.

Extending a server object over creating a generic COM object has the following advantages:

Unlike utility COM objects, server object extensions are registered and configured with specific server objects and are not for ad-hoc use or use with an empty server context.

The process for creating a server object extension is the following:

  1. Create a COM object that implements IServerObjectExtension, and includes any additional custom interfaces and methods. Optionally implement IObjectConstruct, ILogSupport and IObjectActivate.
  2. Optionally create a COM object that implements IAGSSOEParameterPage and ICOMPropertyPage that includes a user form that encapsulates any initialization properties that must be configured for the extension.
  3. Register the server extension COM object created in step 1 on each container machine, the server object manager machine and any client machines (i.e. Web servers, ArcGIS Desktop machines that administer the GIS server).
  4. Register the server object extension with the GIS server (using the AddExtensionType method on IServerObjectAdmin2).
  5. Optionally register the property page created in step 2 in the AGS Extension Parameter Pages component category on each ArcGIS desktop client machine.
  6. Create a new server object configuration that includes the server object extension.
  7. Create your application that consumes the server object and its extension.

Each of these steps will be illustrated using the same example as was used for the utility COM object.

Creating the server object extension

A server object extension is a COM object developed in .NET or C++ that implements IServerObjectExtension and any additional interfaces necessary for its implementation. IServerObjectExtension is a mandatory interface that must be supported by all server object extensions, and includes two methods: Init and Shutdown. This interface is used by the server object to manage the lifetime of the server object extension. The server object cocreates the server object extension and calls the Init method handing it a back reference to the server object via the server object helper argument. The server object helper implements a weak reference on the server object. The extension can keep a strong reference on the server object helper (for example, in a member variable) but should not keep a strong reference on the server object. Extensions should get the server object from the server object helper in order to make any method calls on the server object and release the reference after making the method calls. Init is called once when the instance of the server object extension is created.

The Shutdown method informs the server object extension that the server object's context is being shut down and is about to go away. In response the server object extension should release its reference on the server object helper:

[C#]
private IServerObjectHelper m_SOH;

public void Init(IServerObjectHelper pSOH)
{
  m_SOH = pSOH;
}

public void Shutdown()
{
  m_SOH = null;
}

In your custom methods, you use the server object helper to get a reference to the server object, for example:

[C#]
IMapServer mapsrv = m_SOH.ServerObject as IMapServer;

If your server object extension includes configuration properties or requires any additional intitialization logic, you need to implement IObjectConstruct. IObjectConstruct is an optional interface for server object extensions. The interface includes a single method called Construct. Construct is called once when the server object extension is created, after IServerObjectExtension::Init is called. Construct hands back the configuration properties for the server object extension as a property set. You should include any expensive initialization logic within your implementation of Construct .

While IServerObjectExtension::Init and IObjectConstruct::Construct are called once when the instance of the server object extension is created, if your server object extension requires logic to run each time its server context is acquired and released (each time a client calls CreateServerContext and ReleaseContext), you need to implement IObjectActivate. IObjectActivate is an optional interface for server object extensions that includes two methods: Activate and Deactivate. Activate is called each time a client calls CreateServerContext on the server object extension's server object's context, and Deactivate is called each time a client releases the context (via ReleaseContext). Because Activate and Deactivate are called each time a client gets and releases the server object's context, any logic you implement in these methods should not be expensive.

Finally, if you want your server object extension to log messages to the GIS server's log file, your server object extension should implement ILogSupport. ILogSupport is an optional interface for server object extensions that has a single InitLogging method. InitLogging is called when the server object extension is created and hands back a reference to the GIS server's log object via the log argument. The extension can keep a reference on the server log (for example, in a member variable):

[C#]
private ILog m_log;
public void InitLogging(ILog log)
{
  m_log = log;
}

Since this method is called before IServerObjectExtension::Init and IObjectConstruct::Construct, you can use the log to log messages and errors in initialization, as well as your custom methods:

[C#]
m_log.AddMessage(3,8000,"This is a normal level log message.");
m_log.AddMessage(1,8000,"This is an error level log message.");


Its important to understand the order in which the various methods of the server object extension interfaces are called during initialization and use.

The following example is an extension to a MapServer server object. This server object extension implements IServerObjectExtension, IObjectConstruct and ILogSupport. Its functionality is similar to the ServerUtil object from the previous example, but takes advantage of the additional capabilities of server object extensions. In the previous example, the SumArea method had two arguments: the feature class to query and the query filter. In this example, the feature class to query will come from the LayerName property of the server object extension's configuration, so the only required argument will be the query filter. This property will be obtained in IObjectConstruct::Construct as will a reference to the actual layer.

[C#]
public interface IAreaSum
{
  double sumArea(ref IQueryFilter pQFilter);
}

namespace AreaSumSOE
{
  [AutomationProxy(true), ClassInterface(ClassInterfaceType.AutoDual)]
  public class AreaSumSOE: ServicedComponent, IServerObjectExtension,IObjectConstruct,ILogSupport,IAreaSum
  {
    private IServerObjectHelper m_SOH;
    private ILog m_log;
    private ILayer m_layer;

    public AreaSumSOE()
    {

    }

    //IServerObjectExtension implementation
    public void Init(IServerObjectHelper pSOH)
    {
      m_SOH = pSOH;
    }

    public void Shutdown()
    {
      m_SOH = null;
      m_log = null;
      m_layer = null;
    }

    //IObjectConstruct implementation
    public void Construct(IPropertySet props)
    {
      // read properties, return if it fails
      string lName;
      try
      {
        lName = props.GetProperty("LayerName") as string;
      }
      catch (Exception ex)
      {
        m_log.AddMessage(1,8000,"AreaSumSOE custom error. Error reading properties: " + ex.Message);
        return;
      }

      // find layer, log success or failure
      try
      {
        IMapServer ms = m_SOH.ServerObject as IMapServer;
        IMapServerObjects mso = ms as IMapServerObjects;
        IMap map = mso.get_Map(ms.DefaultMapName);
        UID ltype = new UIDClass();
        ltype.Value = "{E156D7E5-22AF-11D3-9F99-00C04F6BC78E}";
        IEnumLayer el = map.get_Layers(ltype,true);
        el.Reset();

        ILayer l = null;
        while((l = el.Next()) != null)
        {
          if (l.Name == lName)
          {
            m_layer = l;
            break;
          }
        }

        if (m_layer == null)
          m_log.AddMessage(1,8000,"AreaSumSOE error: Layer " + lName + " not found.");
        else
          m_log.AddMessage(3,8000,"AreaSumSOE sucessfully initialized.");

      }
      catch (Exception ex)
      {
        m_log.AddMessage(1,8000,"AreaSumSOE error: Failed to initialize extension: " + ex.Message);
      }
    }

    //ILogSupport implemetation
    public void InitLogging(ILog log)
    {
      m_log = log;
    }

    //IAreaSum implementation
    public double sumArea(ref IQueryFilter pQFilter)
    {
      double dArea = 0;
      try
      {
        IFeatureLayer fl = m_layer as IFeatureLayer;
        IFeatureClass pFClass = fl.FeatureClass;

        // check to make sure these are polygon features
        if (pFClass.ShapeType != esriGeometryType.esriGeometryPolygon)
          return dArea;

        // loop through features and total their areas
        IFeature pFeature = null;
        IArea pArea = null;
        IFeatureCursor pFeatureCursor = pFClass.Search(pQFilter,true);
        while ((pFeature = pFeatureCursor.NextFeature()) != null)
        {
          pArea = pFeature.Shape as IArea;
          dArea += pArea.Area;
        }
      }
      catch (Exception ex)
      {
        m_log.AddMessage(1,8000,"AreaSumSOE error: Error calculating area: " + ex.Message);
      }
      return dArea;
    }

  }
}

Creating the server object extension properties dialog

You'll notice that in this implementation, the layer to query is not passed into the method to sum the areas, instead the layer is a property of the server object extension. This property is set at the time the server object extension is configured by the server administrator. Note that it is valid for this to remain an argument on this method, but for illustrative purposes, it is made a property for this example. To display a form in the Create Server Object wizard in ArcCatalog, and the server object properties page, you can optionally create a COM object that implements IAGSSOEParameterPage and ICOMPropertyPage that includes a user form that encapsulates any initialization properties that must be configured for the extension.

The ICOMPropertyPage interface must be implemented for any property page created for an ArcGIS desktop application. Server object extension property pages must also implement the IAGSSOEParameterPage interface. IAGSSOEParameterPage includes a property for getting and setting the properties for the server object extension (ExtensionProperties), and a property for getting the properties of the server object that is being extended (ServerObjectProperties). The ICOMPropertyPage:Show method will show your form on the create server object wizard. Note that the property page COM object must be registered in the AGS Extension Parameter Pages component category on the machine on which ArcCatalog is running.

The following is an example of a property page that would be displayed in ArcCatalog you would implement for this server object extension. You do this by creating a COM object that implements IAGSSOEParameterPage and ICOMPropertyPage. In this example, the property page exposes a combo box that lists all the polygon feature layers in the map that the server object is serving, which is recorded as the LayerName property in the server object extension's properties.

An example of implementing such a property page is beyond the scope of this discussion, but is illustrated in detail in the Extending a Server Object scenario. The property page will appear on the server object extensions pane of the Add Server Object wizard, and the ServerObject Properties dialog:




Registering the server object extension with ArcGIS Server

The server object extension is registered with ArcGIS Server by doing the following:

The following code shows how you register the server object extension with ArcGIS Server:

[C#]
IGISServerConnection conn = new GISServerConnectionClass();
conn.Connect("seadog");
IServerObjectAdmin2 soa = conn.ServerObjectAdmin as IServerObjectAdmin2;
IServerObjectExtensionType soet = soa.CreateExtensionType();

soet.CLSID = "AreaSumSOE.AreaSumSOE";
soet.Description = "test server obj extension";
soet.Name = "AreaSumExtension";

soa.AddExtensionType("MapServer",soet);

This should be included in a setup program for distributing your custom server object extensions.

Creating a server object configuration that includes your extension

Once you have registered your server object extension with ArcGIS Server, you can create new server object configurations that include your extension either using ArcCatalog (via your server object extension property page (see the example above), or through code.

The following code shows how you can create a new server object configuration with your extension. Note that when you create a server object configuration of a particular type (e.g. MapServer), its configuration includes all extension types which are by default disabled. In this example, the server object extension desribed above is enabled for the Yellowstone MapServer, and the name of the layer to query is "veg":

[C#]
IServerObjectAdmin = soa = pServerConn.ServerObjectAdmin
IServerObjectConfiguration2 soc = soa.GetConfiguration(“Yellowstone”,”MapServer”) as IServerObjectConfiguration2;
soa.StopConfiguration(“Yellowstone”,”MapServer”);

soc.ExtensionEnabled(“AreaSumExtension”) = true;
IPropertySet props = soc.ExtensionProperties(“AreaSumExtension”);
props.SetProperty(“LayerName”,”Veg”);

soa.UpdateConfiguration();
soa.StartConfiguration(“Yellowstone”,”MapServer”);

Creating an application that consumes your server object extension

Once a server object is running in the server that includes your extension, you can write applications that make use of your extension's functionality.

[C#]

The following C# code shows how you can call the methods on the server object extension, given a valid reference to a MapServer (map):

// get the server object extension
IServerObjectExtensionManager extmgr = map as IServerObjectExtensionManager;
IServerObjectExtension SOExt = extmgr.FindExtensionByName(“AreaSumExtension”);
IAreaSum asum = SOExt as IAreaSum;
      
double dTotalArea = asum.TotalAreas(ref sf);
      

[Visual Basic .NET]

The following VB.NET code shows how you can call the methods on the server object extension, given a valid reference to a MapServer (map):

' get the server object extension
Dim extmgr As IServerObjectExtensionManager = ma
Dim SOExt As IServerObjectExtension = extmgr.FindExtensionByName("AreaSumExtension")
Dim asum As IAreaSum = SOEx

Dim dTotalArea As Double = asum.TotalAreas(sf)