Posts Tagged ‘services’

In the first four posts of this series I focussed on everything but building the Appcelerator-based RIA.  Part 1 gave an overview of the technologies, and part 2 described how I added Appcelerator support to an Eclipse web project.  Part 3 was related to implementing the domain model and Spring-based services using Skyway Builder, and part 4 described how the Appcelerator service layer was configured to use the Spring services.  Now the time has come to actually implement the front-end using Appcelerator RIA.  You can download the project and see the running application here. Read the rest of this entry »

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

As I mentioned in part 2, the weather data is coming directly from NOAA.  The NOAA SOAP Web Service is a free web service for accessing all the available weather data.  Unfortunately the web service is difficult to work with.  To me it looks like the web service was implemented based on the architecture of the weather data with minimal regard for the consumer of the service.  Subsequently I had to spend a lot of time figuring out how to parse the data with Groovy (more on that later).

I had to decide exactly how I was going to access the NOAA web service.  I had a couple of choices.  I could use:

  • Groovy – Groovy has some rudimentary capabilities for accessing SOAP web services.  I could use Skyway’s support for Groovy to access the web service.
  • Apache Axis – I could use Axis to generate a client from the WSDL for accessing the web service.  I could take the code generated by Axis and include it in my project and invoke it using Skyway’s Invoke Java step.
  • Skyway Builder Enterprise Edition (EE) – Skyway Builder EE can produce web services (contract-first or contract-last) and consuming SOAP web services.  My intention wasn’t to produce web services, but I could use Skyway Builder EE to integrate with the service using the WSDL.

Since I wanted users of open-source Skyway Builder Community Edition (CE) to be able to study and use this project, I decided against using Skyway Builder EE.  I ultimately decided to see how far I could get with Groovy.  If needed, I could always use Apache Axis.

I was principally concerned with getting weather data from the NOAA web service, but in order to do so I needed to provide coordinates (longitude and latitude) as an input to the service.  I didn’t want the end-user of the application to have to figure out the coordinates for a city on their own, so I used a different operation in the NOAA web service for getting a list of cities and the coordinates for the city.

Now I had a pretty good idea of the the data and operations that are available from the NOAA web service.  So in the modeling project I defined two data types for storing the data that I will get from the service and use in my Appcelerator application. The data types are:

  • Location – for storing city and coordinates
  • WeatherData – for storing forecasted temperatures, humidities, and weather icons.

Data Type: Location

citycoord

Data Type: WeatherData

weather

In order to minimize calls to the NOAA web service, I wanted to load the city and coordinates from the web service only once.  So upon the initial invocation of the web application, the locations would be loaded and persisted to a database.  Subsequent invocations will read the data from the database.  It seamed like a reasonable solution since the location data is just pairs of city names and latitude/longitude coordinates that shouldn’t change.  In order to be able to persist the Location data type, I created the LocationDAO (data store).

For getting the list of cities from the NOAA web service I used NOAA’s latLonListCityNames operation.  I tried to implement the Skyway operation (loadLocations) to use only Groovy for calling the NOAA weather service, but I ran into problems.   So instead I decided to use Apache Axis for calling the service.  I invoke Axis using Skyway’s Invoke Java Step, and I use Groovy’s excellent XML processing capabilities to parse the xml returning from the web service call and map each city/coordinate to a Location bean.  At the end of the Groovy step, I’m left with a collection of Locations (193 in total).  The load action (see screenshot below) show the implementation of the Spring service.  As you can see, in addition to the web service processing logic there’s some additional modeling logic to persist the web service data and to call the web service only if there aren’t any persisted locations in the database.

Operation: loadLocations

loadlocationsaction

For getting the weather forecast for a particular city I used the NOAA’s NDFDgen operation.  Once again I decided to access this operation using Apache Axis and use Groovy for processing the XML.  It took me a while to figure out how to parse the XML, but at the end I figured it out.  I ended up with a simple collection of WeatherData objects.  As you can see from my implementation of the Skyway getWeatherForecast operation, this time I use Skyway’s Invoke Groovy Step for both invoking Axis and parsing the results.  This was done differently for no particular reason…I was just experimenting.

A quick note on Apache Axis.  I had to used a patched version of Apache Axis 1.4, and the WSDL2Java command line tool generated the java code for web service invocation from the WSDL of the NOAA web service.  This wasn’t particularly difficult to do.  Patching Axis was a bit of a pain, and the code generated by WSDL2Java tool was mostly implemented….but not completely implemented.  I copied the generated code for the client stub to the source folder of the web project and finished implementing the stubs in Eclipse.

Operation: getWeatherData

getforecastaction

For reference I downloaded the NOAA SOAP WSDL to the file system and generated the client code using the following command:

C:\j2sdk1.5.0\bin\java -classpath %CLASSPATH% org.apache.axis.wsdl.WSDL2Java GeoCoder.wsdl

From the action diagrams that I modeled in the NOAAWeather project, Skyway Builder:

  • generated to the web project all the Spring Beans representing the logic defined/modeled in the actions
  • generated to the web project all the required Spring configuration files
  • added the required entries to the web.xml
  • added all the required libraries (Spring, Hibernate, etc…) to the web project

With Spring services implemented, the next step is to tie the Spring beans into Appcelerator which I’ll cover in part 4, where I will cover how this Spring services are made accessible as remote services to an Appcelerator-based web application.  Stay tuned!

<shameless-plug>Did you notice that I didn’t spend any time specifying anything related Spring?  This was all generated for me by Skyway Builder.  Clean and concise Spring code that’s blending effortlessly with java code originating from other sources (i.e. Axis) and Groovy scripting.  :-)</shameless-plug>

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

In part 1 (Appcelerator RIA + Spring Services) I gave an overview of the technologies that I used for my Appcelerator learning exercise, including Spring, Skyway Builder, and Appcelerator (of course).  I should have probably described the application that I create too.  I built an application that lets the end-user of the application get a five day weather forecast for a city that they select.  For this application:

  • the weather data comes from a NOAA web service that is accessible using SOAP
  • the Spring Services are defined and generated from Skyway Builder
  • the web application is implemented using Appcelerator

The Appcelerator website is the best resource for installing Appcelerator, so I won’t repeat the steps here.  Suffice it to say that Appcelerator is pretty simple to install, and the principle mechanism for interacting with the Appcelerator SDK is the command-line interface (CLI).

Appcelerator also offers some Eclipse-based tooling, but I wasn’t able to get it working.  At first I thought it might be colliding with the Skyway Builder plugins, but Appcelerator plugins didn’t work with a vanilla Eclipse 3.3 installation either.  The Appcelerator plugins are no longer prominently highlighted on the Appcelerator website, so I’m thinking it may no longer be maintained.  I asked on the Appcelerator forums whether Eclipse plugins were even supported any longer, but I never got a response.  As a matter of fact, I didn’t get any response to any of my forum questions.  Bummer!

Undeterred….I proceeded on my own.  I created a new Skyway project called NOAAWeather, which resulted in two Eclipse projects: a modeling project (NOAAWeather) and a standard Eclipse web project (NOAAWeather-Web). My goal was for Accelerator to generate the basic structure of a web application, and I would copy the structure into the web project.

Side note: This series of blog posts assumes that the reader has some basic familiarity with Skyway Builder and Spring.  If you aren’t familiar with Skyway Builder, there are a variety of resources available on the Skyway Community website, including videos, tutorials, and sample applications.

Using the Appcelerator CLI, I specified the following:

C:\java\appceleratortest>app create:project . myapp java
Connecting to update server …
Fetching release info from distribution server…
Using service java 1.0.19
Using websdk 2.2.2
Installing components … (this may take a few seconds)
Appcelerator Java project created … !

The SDK created a folder called myapp which had several sub-folders.  Using the contents created by the SDK, here’s what I did to appcelerate my web project (fyi…appcelerate is not an official appcelerator term…but maybe the should consider it…lol):

  1. copied myapp\templates\web.xml to WEB-INF\web.xml in Eclipse web project
  2. copied java libraries (myapp\lib\*.jar,  myapp\lib\compiler\xerces-2.7.1.jar, myapp\lib\compiler\xml-apis-2.7.1.jar) to WEB-INF\lib in Eclipse web project.
  3. copied myapp\app\services\org folder to src folder of Eclipse web project
  4. copied myapp\public folder to the WebContent folder of the Eclipse web project

I deployed the web project (to Apache Tomcat) and made sure Service Tester worked.  I was confident that the basic pieces were in place.  The next step (part 3) was to start using Skyway modeling to implement and generate the Spring services that would be used by my application.

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