April 03, 2012

CSW Server Implementation (Part III)

Last time we laid the basis for a rough CSW server implementation. The main missing point to it is the ability to plug it to a database, which would make it much more useful…

But before doing this, I will take some time to polish our server a little bit so that it is easier to test, and more compliant to the CSW standard. These are hardly complicated tasks, but they are necessary and other posts are long enough already. So here we go:

  • Support the GET+XML protocol.
  • Complete the GetCapabilities operation.

The GET+XML protocol

What we need here is another service, set to another WCF entry point. As we will see, this service will be reusable as is for every OGC web service you wish to implement using GeoSIK. Thus we will call it Ows:

image

Add a reference to the System.ServiceModel.Web assembly, and just add a simple operation to the contract:

[ServiceContract]
public interface IOws {

[OperationContract]
[FaultContract(typeof(GeoSik.Ogc.Ows.V100.Types.ExceptionReport), Name="ExceptionReport")]
[WebGet(UriTemplate="?service={service}&version={version}&request={request}", BodyStyle=WebMessageBodyStyle.Bare, RequestFormat=WebMessageFormat.Xml, ResponseFormat=WebMessageFormat.Xml)]
Message Execute(string service, string version, string request);
}

This is quite simple, and you can recognize the 3 mandatory parameters that are common to all OGC web services (cf. specification §9.2.1). Now we have our contract, let’s implement it using the GeoSik.Ogc.Ows.ServiceLocator class. This class is able to find OWS implementations in an assembly and dynamically call its services. To make it work, just add an attribute to the Discovery service we implemented last time:

[GeoSik.Ogc.OwsDescription(Discovery.Service, Discovery.Version)]
public class Discovery:
GeoSik.Ogc.WebCatalog.Csw.V202.Discovery
{
// ...
}

Now use the ServiceLocator to find and call this service at runtime:

public class Ows:
IOws
{

public Message Execute(string service, string version, string request)
{
NameValueCollection parameters=WebOperationContext.Current.IncomingRequest.UriTemplateMatch.QueryParameters;

var locator=new GeoSik.Ogc.Ows.ServiceLocator(Assembly.GetExecutingAssembly());
IXmlSerializable response=locator.InvokeService(parameters);

return CreateMessage(response, OperationContext.Current.IncomingMessageVersion);
}

private static Message CreateMessage(IXmlSerializable response, MessageVersion version)
{
StringBuilder serialized=new StringBuilder();
using (var xw=XmlWriter.Create(serialized))
response.WriteXml(xw);
var xr=XmlReader.Create(new StringReader(serialized.ToString()));
return Message.CreateMessage(version, "ExecuteResponse", xr);
}
}

Our service is implemented. We just have to tie it to a proper WCF endpoint in the Web.config file:

<service name="Tutorial.Ows" behaviorConfiguration="ows100PoxBehavior">
<endpoint address="" contract="Tutorial.IOws" binding="webHttpBinding" behaviorConfiguration="poxBehavior" />
</service>

That’s it. Let’s try to start the application and call the GetCapabilities operation at http://localhost:6155/Ows.svc/?service=CSW&version=2.0.2&request=GetCapabilities (your port number may vary). Works on my machine


The GetCapabilities operation


Now you can see the result of a GetCapabilities call, you can see that no operation is defined. The main reason for this is that GeoSIK is unable to detect the address of our services. We will have to help it out by implementing our own GetCapabilitiesProcessor class. Just override the GetEndPoints method and provide the proper parameters for our 2 services:

public class GetCapabilitiesProcessor:
GeoSik.Ogc.WebCatalog.Csw.V202.Discovery.GetCapabilitiesProcessorBase
{

public GetCapabilitiesProcessor(Discovery service):
base(service)
{ }

protected override IEnumerable<GeoSik.Ogc.OwsEndPoint> GetEndPoints()
{
var ret=new List<GeoSik.Ogc.OwsEndPoint>();

Uri baseUri=new Uri(
string.Format(
CultureInfo.InvariantCulture,
"{0}://{1}/",
WebOperationContext.Current.IncomingRequest.UriTemplateMatch.BaseUri.Scheme,
WebOperationContext.Current.IncomingRequest.UriTemplateMatch.BaseUri.Authority
)
);

ret.Add(
new GeoSik.Ogc.OwsEndPoint() {
AddOperationName=false,
BaseUri=new Uri(baseUri, "Ows.svc/"),
Method=GeoSik.Ogc.OwsMethod.Get
}
);
ret.Add(
new GeoSik.Ogc.OwsEndPoint() {
AddOperationName=true,
BaseUri=new Uri(baseUri, "Csw.svc/xml/"),
Method=GeoSik.Ogc.OwsMethod.Xml
}
);

return ret;
}
}

Make it the processor to use in the Discovery service:

protected override Discovery.GetCapabilitiesProcessorBase CreateGetCapabilitiesProcessor()
{
return new GetCapabilitiesProcessor(this);
}

Try the GetCapabilities operation again: we are compliant! If you delve a bit more in the GeoSIK libraries you will notice that there are many other opportunities for customization for this operation and that every other operation can be customized in pretty much the same way we just customized GetCapabilities. This gives you (the developer) all the power you need to implement the CSW server you have been dreaming of all this time (I may be a bit carried away here…).


Enjoy your CSW server for now. As is now usual, the complete source code for this tutorial can be downloaded here. Next time we will plug our server to a proper database: get set for some Sql Server and Entity Framework adventures!…

No comments:

Post a Comment