Wednesday, February 18, 2009

Interoperability – F# + Silverlight 2 on Apache + Metro Web Services on Tomcat

I bought the books Silverlight 2 in Action and Pro Silverlight 2 in C# 2008 for a while now and did not have a chance to work with Silverlight for the past several weeks.  However, recently I had to investigate using GlassFish’s Metro library to build web services as it was highly recommended by colleagues.  I have previous familiarity with using Apache Axis to build web services as that web services stack had strong plugin support with Eclipse, my IDE of choice when it comes to Java development.  I thought this was a great opportunity to build an sample web service using Metro and build the web service client on Silverlight platform.

My sample web service code was intentionally simple and the java code is as follows:

package sample.ws.metro;

import javax.jws.WebService;
@WebService public class HelloWorldService {
public String hello(String name) { return "Hello, " + name;
}

}

I followed the instructions on the web to create the applicationContext.xml and web.xml file and deployed it to my Tomcat server.  However, I had a hard time getting this simple example to work.  In the end, I downloaded Netbeans and created the web service solution in that IDE.  Netbeans generated the applicationContext.xml and web.xml file for me and it worked like a charm.  Once I have those configuration files, I could easily do subsequent changes in Eclipse without any problems.  This demonstrated how tool support can make life a lot easier for developers.  Once the initial setup is completed, it’s much simpler to build and modify from a working solution.  Here are the configuration files for those who might be interested:

applicationContext.xml:


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:ws="http://jax-ws.dev.java.net/spring/core"
xmlns:wss="http://jax-ws.dev.java.net/spring/servlet"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://jax-ws.dev.java.net/spring/core http://jax-ws.dev.java.net/spring/core.xsd
http://jax-ws.dev.java.net/spring/servlet http://jax-ws.dev.java.net/spring/servlet.xsd">

<wss:binding url="/helloworld">
<wss:service>
<ws:service bean="#HelloWorldService">
<ref bean="#HelloWorldService">
</ref>
</ws:service>
</wss:service>
</wss:binding>

<!-- this bean implements web service methods -->
<bean id="HelloWorldService" class="sample.ws.metro.HelloWorldService" />
</beans>

 web.xml:


<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<display-name>metropilot</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<!-- this is for Spring -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>*.htm</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
<!-- these are for JAX-WS -->
<servlet>
<servlet-name>jaxws-servlet</servlet-name>
<servlet-class>com.sun.xml.ws.transport.http.servlet.WSSpringServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>jaxws-servlet</servlet-name>
<url-pattern>/ws</url-pattern>
</servlet-mapping>
</web-app>

Similarly, on the Silverlight side…I needed the tools support in Silverlight on Visual Studio 2008 to help me generate the Service Reference library.  I opened up Visual Studio 2008, right-click my Silverlight library project and chose Add Service Reference… and then put in the url of my web service, which in my case was hosted at http://localhost:8080/metropilot/ws/hello?wsdl.  I would then build it as a Silverlight library that I can reference in the F# project.  I assume and hope that when Visual Studio 2010 arrives, I can do all of this without leaving the F# environment.  Now I can write my F# Silverlight client which asks the user to type in a name that will be sent to the web service and display the returned greeting.  The F# client code is as follows:


#light
namespace SilverLightFSharp

open System
open System.Windows
open System.Windows.Controls
open System.ServiceModel
open List

// Data Binding example
type MyPage = class
inherit UserControl

new () as this = {} then

let sp = new StackPanel()

let textblock = new TextBlock()
textblock.Text <- "Please enter your name:"
sp.Children.Add(textblock)

let textbox = new TextBox()
sp.Children.Add(textbox)


let button = new Button(Content="Get Greeting...")
sp.Children.Add(button)

button.Click.Add(fun e ->
let binding = new BasicHttpBinding();
let endpoint = new EndpointAddress("http://localhost:8080/metropilot/ws/hello?wsdl");
let service = new ServiceLibrary.MetroPilotService.HelloWorldServiceClient(binding,endpoint)
service.helloCompleted.Add(fun e ->
textblock.Text <- e.Result.ToString()
service.CloseAsync())
service.helloAsync(textbox.Text))

this.Width <- 400.0
this.Height <- 300.0
this.Content <- sp
end

type MyApp = class
inherit Application

new () as this = {} then
this.Startup.Add(fun _ -> this.RootVisual <- new MyPage())
//base.Exit.Add( fun _ -> ()) //this.Application_Exit)
//this.InitializeComponent()
end

My application.xap file has the following files:

  • AppManifest.xaml
  • FSharp.Core.dll
  • ServiceLibrary.dll – this was the library generated using Visual Studio 2008 via Add Reference…
  • SilverlightFSharp.dll – the compiled client code

Since I’m running this Silverlight client on an Apache web server that’s running on port 8000 and my web services was running on a Tomcat server on port 8080, I had to create the clientaccesspolicy.xml and crossdomain.xml file and put it in the root folder of the Tomcat server or the Silverlight client would refuse to access the web service on my Tomcat server. 

My clientaccesspolicy.xml is as follows:


<?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>

My crossdomain.xml file is as follows:


<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
<allow-http-request-headers-from domain="*" headers="*"/>
</cross-domain-policy>

Finally, after all these configurations, I got my F# Silverlight 2 client code running on Apache web server talking to my Java based web services running on Tomcat.

1 comment:

appdynamics integration said...

Thanks for sharing the real crakers to handle projet stuff in an intelligent and cohesive way !