In this post I will make the Skyway Services from (part 3) available as Appcelerator services that can be used by an Appcelerator-based RIA application. I was hoping I could just use Spring services, but at SpringOne 2008, Kevin Whinery said the recommended approach is to use Appcelerator services and wire in Spring services. It’s an extra step, but it turns out that it gave me a place where I could workaround some issues I had with Appcelerator’s automatic JSON serialization logic. While I could’ve updated the Spring beans to return JSON to the Appcelerator service, I wanted to avoid tweaking my Spring services to accomodate Appcelerator.
Appcelerator has it’s own implementation of services. Appcelerator services are basically just plain java classes, with the @Service annotation. Appcelerator provide a servlet that all remote service requests will go to. Depending on the requested service, the servlet will delegate to the appropriate service class. I used Appcelerator’s sample service (EchoService) as the starting point for my own project. First I defined a few operations, and then I verified using Service Tester that I could send requests and receive responses from these operations. It took a little trial and error, but I got the hang of it.
The next step was to tie in the Spring-based services that I created in Part 3. This is where it got interesting. As part of generating or scaffolding Spring MVC applications, Skyway Builder creates all the required Spring configuration files and automatically adds all the required configurations to the web.xml file. In the case of Appcelerator based applications, the updates to the web.xml file weren’t needed because the Appcelerator servlet is fronting all the Appcelerator services….which will subsequently call the Spring services. So I ripped out the web.xml configurations generated by Skyway, and I added my own boilerplate Spring code the Appcelerator services.
22: private BeanFactory factory;
23:
24: public WeatherService()
25: {
26: String[] configFiles = new String[]
27: { "NOAAWeather-generated-web-context.xml",
28: "NOAAWeather-generated-service-context.xml",
29: "NOAAWeather-generated-domain-context.xml",
30: "NOAAWeather-generated-dao-context.xml"};
31:
32: setFactory(new ClassPathXmlApplicationContext(configFiles));
33:
34: }
Next I added two methods (operations) for the services that I wanted to make available to my Appcelerator-based RIA application. The first method is getLocations. Using the Appcelerator @Service annotation, all requests to “app.locations.get.message.request” will be served by this method.
36: @Service(request = "app.locations.get.message.request",
37: response = "app.locations.get.message.response",
38: version = "1.0")
39: protected void getLocations (Message request, Message response) throws Exception
40: {
41: Set<CityCoordinates> locations = null;
42: try {
43: locations = ((noaaweather.service.weather.WeatherService) getFactory().
44: getBean("WeatherService")).loadLocations();
45:
46: }
47: catch(Exception e)
48: {
49: System.out.println("EXCEPTION = "+e.toString());
50: }
51: net.sf.json.JSONArray ja = net.sf.json.JSONArray.fromObject(locations);
52:
53: response.getData().put("locations", ja);
54: return;
55:
56: }
As you can see, this method will call the loadLocations method of the WeatherService bean that was implemented and generated using Skyway Builder in Part 3. There aren’t any inputs, so the Appcelerator input message (request) is ignored. The collection of Location objects is converted to JSON (using Json-Lib) and loaded into the response message. Appcelerator is supposed to have some support for automatically converting java types to JSON, but unfortunately I couldn’t get it to work for collections. So I had to do the conversion myself in code.
Next I added the getForecast method, which will serve all requests to “app.forecast.get.request”.
58: @Service(request = "app.forecast.get.request",
59: response = "app.forecast.get.response",
60: version = "1.0")
61: protected void getForecast (Message request, Message response) throws Exception
62: {
63: Set<WeatherData> forecast = null;
64:
65: Calendar now = Calendar.getInstance();
66: Calendar later = Calendar.getInstance();
67: later.add(Calendar.DATE, 5);
68: try {
69: forecast = ((noaaweather.service.weather.WeatherService) getFactory()
70: .getBean("WeatherService"))
71: .getWeatherForecast((String)request.getData().getString("citystate"),now,later);
72: }
73: catch(Exception e)
74: {
75: System.out.println("EXCEPTION = "+e.toString());
76: }
77: net.sf.json.JSONArray ja = net.sf.json.JSONArray.fromObject(forecast);
78: response.getData().put("forecast", ja);
79: return;
80: }
This method will call the getWeatherForecast method of the WeatherService bean. This method will take the citystate parameter that should be included in the request and pass it as an input parameter. This method will also calculate the two input dates (today’s date and the date five days from now). Eventually I can have the dates selected by the end-user and passed in as request parameters. Once again I converted the WeatherData objects to JSON before loading it into the response.
That’s pretty much it for the Appcelerator service. In the next part I will review the implementation of the web application front-end.
Here’s a list of my series of posts related to my Appcelerator/Spring/Skyway project:
* Part 1: Appcelerator RIA + Spring Services
* Part 2: Adding Appcelerator support to a web project
* Part 3: Spring Services and NOAA Web Service
* Part 4: Implementing Appcelerator Services using Spring
* Part 5: Building the Weather App with Appcelerator RIA