This tip is about self hosting of services with the .Net WCF or Java Restlet framework. Cross-domain access is mandatory here since the Site of Origin does not host the services.

Self hosting a service represents a quick & easy way to expose a Web Service for tests purpose. Yet, customization is extermely limited since self hosting does not provide you all the capabilities of Web Servers such as delivering static content (cross domain policy file).

The first thing you need to know is that when Silverlight attempts to access services in cross-domain, it will send a HTTP GET method on http://<domain:port>/clientaccesspolicy.xml or http://<domain:port>/crossdomain.xml (in case clientaccesspolicy.xml wasn’t found). If you plan to self host your Web Service, you will agree that there is no possible way to host a physical Cross Domain policy file at the root of the Web Server.

You must handle this request through your Service implementation. It usually matches the /clientaccesspolicy.xml or /crossdomain.xml URI pattern.

WCF way (inspired from Carlo’s blog)

Self-hosted Service

class Program
{
    static void Main(string[] args)
    {
        // URI of the Service
        Uri uri = new Uri("http://localhost:8081/");
 
        // create a new WebServiceHost specifying the implementation of the Service
        // and the specified uri to bind with
        WebServiceHost host = new WebServiceHost(
                new REST_ItemsServiceLibrary.ItemsREST(),
                uri);
 
        // add the Service Endpoint specifying the Service Interface
        host.AddServiceEndpoint(typeof(REST_ItemsServiceLibrary.IItemsREST),
                                new WebHttpBinding(), "");
        // open the WebServiceHost
        host.Open();
 
        Console.WriteLine("Service hosted at {0}", uri.OriginalString);
        Console.ReadLine();
 
        // close the WebServiceHost
        host.Close();
 
    }
}

Handling cross-domain file requests 

As we said earlier, we have to manage the /clientaccesspolicy.xml, specify in the Service Interface that HTTP GET method on the URI /clientaccesspolicy.xml will be handled by GetSilverlightPolicy();

[OperationContract]
[WebGet(UriTemplate ="/clientaccesspolicy.xml")]
Stream GetSilverlightPolicy();

this also applies to /crossdomain.xml

Finally, in your Service implementation, return a clientaccesspolicy.xml content:

public Stream GetSilverlightPolicy()
{
    string result = @"<?xml version=""1.0"" encoding=""utf-8""?>
<access-policy>
    <cross-domain-access>
        <policy>
            <allow-from http-request-headers=""*"">
                <domain uri=""*""/>
            </allow-from>
            <grant-to>
                <resource path=""/"" include-subpaths=""true""/>
            </grant-to>
        </policy>
    </cross-domain-access>
</access-policy>";
    return StringToStream(result);
}
Stream StringToStream(string result)
{
    WebOperationContext.Current.OutgoingResponse.ContentType = "application/xml";
    return new MemoryStream(Encoding.UTF8.GetBytes(result));
}

Restlet way

Self-hosted Service

public static void main(String[] args) {
    try {
        // Create a new Component.
        Component component = new Component();
        // Add a new HTTP server listening on port 8182.
        component.getServers().add(Protocol.HTTP, 8182);
        
        // Attach the sample application.
        component.getDefaultHost().attach(
                new FirstResourceApplication(component.getContext()));
        
        // Start the component.
        component.start();
        
    } catch (Exception e) {
        // Something is wrong.
        e.printStackTrace();
    }
}

Handling cross-domain file requests 

Following the same philosophy as the WCF way, we need to handle HTTP GET method on /clientaccesspolicy.xml file request :

In the Application implementation you must bind the URI pattern with the Ressource that will handle it :

image

Finally, implement the ClientAccessPolicy class, specially the HTTP GET method within getRepresentation method :

package firstResource;
 
import java.io.IOException;
 
import org.restlet.Context;
import org.restlet.data.MediaType;
import org.restlet.data.Request;
import org.restlet.data.Response;
import org.restlet.resource.DomRepresentation;
import org.restlet.resource.Representation;
import org.restlet.resource.Resource;
import org.restlet.resource.Variant;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
 
public class ClientAccessPolicy extends Resource{
 
    public ClientAccessPolicy(Context context, Request request,
            Response response) {
        super(context, request, response);
        getVariants().add(new Variant(MediaType.TEXT_XML));
    }
    
    @Override
    public Representation getRepresentation(Variant variant){
        if (MediaType.TEXT_XML.equals(variant.getMediaType())) {
             try {
                DomRepresentation representation = new DomRepresentation(
                      MediaType.TEXT_XML);
                // Generate a DOM document representing the list of
                // items.
                Document d = representation.getDocument();
                Element accesspolicy = d.createElement("access-policy");
                d.appendChild(accesspolicy);
                
                Element crossdomainaccess = d.createElement("cross-domain-access");
                accesspolicy.appendChild(crossdomainaccess);
                
                Element policy = d.createElement("policy");
                crossdomainaccess.appendChild(policy);
                
                Element allowfrom = d.createElement("allow-from");
                allowfrom.setAttribute("http-request-headers","*");
                
                Element domain = d.createElement("domain");
                domain.setAttribute("uri", "*");
                
                allowfrom.appendChild(domain);
                
                Element grantto = d.createElement("grant-to");
                Element resource = d.createElement("resource");
                resource.setAttribute("include-subpaths","true");
                resource.setAttribute("path", "/");
                
                
                grantto.appendChild(resource);
                
                policy.appendChild(allowfrom);
                policy.appendChild(grantto);
                
                d.normalizeDocument();
 
 
                // Returns the XML representation of this document.
                return representation;
             } catch (IOException e) {
                e.printStackTrace();
             }
          }
 
          return null;
    }
 
}

- Ronny Kwon