OpenShift is a Platform As A Service (Paas) offering from Red Hat joining the likes of Heroku, GAE, Jelastic and others. OpenShift supports Java EE, PHP, Node.js, Ruby, Python and Perl applications. I tried out deploying a simple Java EE application based on ZK Framework, interested people can have a look at the application running live here.
This article would cover:
- Setting up of OpenShift account, Maven and creating an application on cloud
- Developing and deploying the application on cloud
also read:
- Java EE Tutorials
- Java EE 7 Tutorials
- Java EE Interview Questions
Setting up OpenShift Account, Creating application
First step to setting up of application is to create an account on the OpenShift website here. Once you have registered, you have to download and install the Command line tool for OpenShift cloud control which is Redhat Console(rhc). I used the Ruby gems to install the RHC tool which is as simple as:
$ sudo gem install rhcCreating a domain and application
All the applications deployed on OpenShift are in a namespace which is nothing but the domain you create. Domain names have to be unique and are part of the URL to access the application.
#Creating a domain $ rhc domain create -l<login> -n<domain name> Password:<password>Once the domain has been successfully created, the next step is to create an application:
#Creating an application $rhc app create -a <application_name> -l <loginname> -t jbossas-7.0-t option is used to specify the type of the application, which can be any one of the following:
php-5.3 — for PHP applications
wsgi-3.2 — for Web Server Gateway Interface applications
rack-1.1 — for Ruby Webserver Interface applications
perl-5.10 — for Perl applications
jbossas-7.0 — for JBoss AS applications (used for ZK project)
raw-0.1 — a raw cartridge type used to create applications of no specific typeA Cartridge is the other infrastructure elements that your application would use and these can be added using the same rhc app command. In our application we would not be using any of those and hence not being added.
The same operations can be performed from their web console as well. An advantage of using the command line is that once you create an domain using rhc command line, it adds the necessary ssh-keys, and also while creating the application it sets up the git repository as well. Deploying to OpenShift is as simple and pushing the application to the remote git repository, this is similar to the approach in Heroku.
Setting up Maven
The application is nothing but an Maven project. So if you are interested to run the application locally then you need to setup Maven as well. Download the Maven package from here, and setup the M2_HOME environment variable to the Maven home i.e the folder where the Maven package was extracted and also update the PATH variable to the M2_HOME/bin.
$mvn -version Apache Maven 3.0.4 (r1232337; 2012-01-17 14:14:56+0530) Maven home: /home/mohamed/maven Java version: 1.7.0, vendor: Oracle Corporation Java home: /home/mohamed/jdk1.7.0/jreDeveloping and Deploying the Application
We would be using ZK framework for our sample application. The application reads from a JSON source about the Marathon Events in India and plots them on a Google Map. For reading the JSON source we would be using the GSON library.
We have to update the contents of pom.xml to reflect the ZK, GSON and other dependencies. One can even use Eclipse to develop the application and that is exactly what I did:$ mvn eclipse:eclipseand then load the project in eclipse. Also Eclipse should be able to resolve the libraries used in the application i.e all the ZK jars and the GSON jar. If you find any issue where eclipse complains about not finding the jars, then you must add a variable M2_REPO to Eclipse environment variables which would point to the directory where Maven downloads all the jars.
The JSON source for the marathon events is constructed from the details available on RunInfinity website.There are few things to be done to plot the above data on a map:
1. Construct Gmarker data from the above data.
2. Obtain the Latitude and Longitude data from the location information. For this we make use of the Google Geocoding API.The Model which holds the Marathon event information is:
public class MarathonEvent { @SerializedName('event_url') String url; String name; String date; String location; Location geoLocation; String formattedContent; @Override public String toString(){ if ( formattedContent == null ){ StringBuilder builder = new StringBuilder(); builder.append('Date: '+date); builder.append('<br/>'); builder.append('<a href=\''+url+'\'>'+name+'</a>'); builder.append('<br/>'); builder.append(geoLocation.name); formattedContent = builder.toString(); } return formattedContent; } }Parsing the JSON source for Marathon Events list where the JSON source is marathon_events.json:
InputStream marathonEvenJsonSource = getClass().getResourceAsStream("marathon_events.json"); Gson gson = new Gson(); JsonReader jsonReader = new JsonReader( new InputStreamReader(marathonEvenJsonSource)); JsonParser jsonParser = new JsonParser(); JsonArray jsonArray = jsonParser.parse(jsonReader).getAsJsonArray(); for ( JsonElement element : jsonArray){ MarathonEvent marathonEvent = gson.fromJson(element, MarathonEvent.class); marathonEvent.geoLocation = getGeoCode(marathonEvent.location); Gmarker gmarker = new Gmarker(marathonEvent.location, marathonEvent.geoLocation.latitude, marathonEvent.geoLocation.longitude); gmarker.setContent(marathonEvent.toString()); marathonEvents.add(gmarker); }if you are not able to follow the JSON parsing code, please read our previous article regarding the same here.
The interesting part here is the
getGeoCode(marathonEvent.location)
call which is defined as:public Location getGeoCode(String location) throws MalformedURLException, IOException{ String url = 'http://maps.googleapis.com/maps/api/geocode/json?address='+URLEncoder.encode(location, 'UTF-8')+'&sensor=false'; Location geoLocation = new Location(location); Gson gson = new Gson(); InputStream in = new URL(url).openStream(); JsonReader reader = new JsonReader(new InputStreamReader(in)); JsonParser parser = new JsonParser(); JsonObject jsonObject = parser.parse(reader).getAsJsonObject(); //Fetch all the possible latitude/longitude results for this place JsonArray resultsArray = jsonObject.getAsJsonArray('results'); if ( resultsArray.size() > 0 ){ //Generally there's only one result, so pick the first. JsonObject geoInformation = resultsArray.get(0).getAsJsonObject(); String formattedAddress = geoInformation.get('formatted_address').getAsString(); JsonObject locationJsonObj= geoInformation.get('geometry') .getAsJsonObject() .get('location') .getAsJsonObject(); String latitude = locationJsonObj.get('lat').getAsString(); String longitude = locationJsonObj.get('lng').getAsString(); geoLocation.latitude = Double.parseDouble(latitude); geoLocation.longitude = Double.parseDouble(longitude); geoLocation.name = formattedAddress; } return geoLocation; }The above code parses the JSON response obtained from the Google Places API and retrieves the formatted_address field and the latitude(lat) and longitude(lng) values. Here as well the GSON Library is used for parsing. The above two code snippets are part of the MarathonEventsSource class which is our source for model information to the views.
Now lets create a ZK based View and Controller/View Model class.
ZK Based View
<zk> <script type='text/javascript' content='zk.googleAPIkey='AIzaSyAgYZVjw5TQWGxAMFv0ayA8OlAtVFkyx0'' /> <window id='main' width='00%' height='00%' apply='net.javabeat.MarathonHomeComposer'> <borderlayout> <north size='70px'> <div> <html><![CDATA[ <h style='color:#0C7A9A;margin-left:0px'>Marathon Events in India</h> ]]></html> </div> </north> <center flex='true'> <div id='contentDiv' > <gmaps id='eventsMap' width='00%' height='00%' showSmallCtrl='true'> <attribute name='onMapClick'><![CDATA[ org.zkoss.gmaps.Gmarker gmarker = event.getGmarker(); if (gmarker != null) gmarker.setOpen(true); ]]></attribute> </gmaps> </div> </center> <south size='0px'> <hbox pack='center' width='00%'> <html><![CDATA[ <span style='color:#0C7A9A'>Developed by Mohamed Sanaulla</span> ]]></html> </hbox> </south> </borderlayout> </window> </zk>Interesting pieces from the above code:
- ZK provides different layouts for organising the contents and BorderLayout is one such layout. In this the content can be organized into 4 regions (exactly similar supported by Swing).
- Gmaps is the component for embedding the Google Maps.
- Each view can be backed a View Model class by using the apply attribute of window component
apply="net.javabeat.MarathonHomeComposer"
Lets have a look at the MarathonHomeComposer class which is the View Model/Controller class.
public class MarathonHomeComposer extends GenericForwardComposer{ MapModel mapModel; Gmaps eventsMap; Div contentDiv; public MarathonHomeComposer() throws MalformedURLException, IOException{ String marathonEventScheduleSource = 'http://runinfinity.com/calendar/india-marathon-calendar'; MarathonEventsSource marathonEventsSource = new MarathonEventsSource(); List<Gmarker> marathonEvents = marathonEventsSource.getMarathonEvents(); mapModel = new MapModelList(marathonEvents); } public void doAfterCompose(Component comp) throws Exception { super.doAfterCompose(comp); eventsMap.setModel(mapModel); eventsMap.setZoom(9); } }Few observations from the View Model class:
- It extends the GenericForwardComposer class which supports binding of the components between the View and the View Model based on the ids. For example- gmaps component has an id=”eventsMap” and the View Model MarathonHomeComposer class has an instance field by name: eventsMap. This is an easier way of binding the UI element to the Java layer, the other ways can be by using Annotations.
- The model for the Gmaps is MapModel which is constructed using the MapModelList and the List obtained from the JSON parser (MarathonEventsSource class)
- doAfterCompose method would have all the components (read Gmaps) initialized and hence we set the model in this method.
If you are interested in the source of this application, you can get it from the git repo here.
Deploying the application to OpenShift
As I said earlier, deploying the application is as simple as running git push command. If you run:
$ git remote -v origin ssh://ad221d298243438491c77f9cd9a83206@marathons-mylearning.rhcloud.com/~/git/marathons.git/ (fetch) origin ssh://ad221d298243438491c77f9cd9a832[email protected]/~/git/marathons.git/ (push)The remote named origin points to the git repo in the cloud. Deploying the application to OpenShift platform:
$ git push origin # other output remote: [INFO] BUILD SUCCESS remote: [INFO] ------------------------------------------------- remote: [INFO] Total time: 30.888s remote: [INFO] Finished at: Tue May 29 12:24:43 EDT 2012 remote: [INFO] Final Memory: 11M/111M remote: [INFO] ------------------------------------------------- #other outputOnce you have deployed the application, you can access the application by visting: http://-.rhcloud.com/marathonHome.zul
Let me summarize this huge post into:
- Constructing the Model source for the Map- this involves reading the JSON source, obtaining the latitude and longitude information and then constructing the Gmarker objects.
- Construct the view- marathonHome.zul which has the gmaps component.
- Wire the View with the View Model class and also wire up the gmaps component with the model.
This sample application can be accessed here.
Some of the useful resources for this sample: