Lab 5 – Google Maps In this lab you will use the results of Lab 3 to display the location of the cellular phone in a Google Map in real-time. You will use the part of Lab 3 that obtains GPS fixes every 10 seconds and sends them using UDP to the database on a server. Objective The objective to this lab is to: 1. Create a Google Web Toolkit Project 2. Learn how to use Remote Procedure Calls (RPC) in GWT 3. Learn how to use the Google Maps to create a simple mapping application Experiment 1 Writing the server-side code In this part you will create the required NetBeans project to develop a Google Map application. To get started with the NetBeans project, do the following steps: 1. Run NetBeans 2. Create a new project (File -> New Project) 3. Choose Java Web -> Web Application and Click Next 4. Write in your project name, e.g., Lab 5 and select the appropriate path where to store it 5. Select the server where you will be deploying your application and the Context Path for your app. Click Next and Finish 6. Your Lab 5 project appears in the upper left window 7. Right Click in project’s name and in the menu click Properties 8. In the categories list, click Libraries 9. Click Add JAR/Folder button, go to your gwt installation folder, select the gwtservlet.jar and click open Page 1 of 24 10. Click Add JAR/Folder button, go to your Postgres installation folder, look for the jdbc folder and select the posgis_1.3.3.jar. Click Open and then Click Ok. 11. Right Click on Source Packages, and in the menu that appears, click New>Package. 12. Write the package name as cse.usf.edu.lab5.client.services and click finish 13. Repeat steps 11 and 12, with the new package name being cse.usf.edu.lab5.client.entities In this exercise you will write the server-side code for the Google Map application. This code will create the server-side methods that will invoke the database to obtain the last location stored in the database of the active users. 1. Right click in the package cse.usf.edu.lab5.client.entities and in the menu click New->Java class 2. Write down the name of the class as TrackingUpdate. Click finish 3. Select all the code that appears in the TrackingUpdate.java screen and delete it 4. Copy and paste the following code in that area and save it (File->Save) Listing 1- TrackingUpdate.java package cse.usf.edu.lab5.client.entities; import com.google.gwt.user.client.rpc.IsSerializable; // A GWT serializable class used to encapsulate the information about active sessions public class TrackingUpdate implements IsSerializable{ private double latitude; private double longitude; private int sessionid; private String username; public TrackingUpdates(){ } public int getsessionId(){ return sessionid; } public String getUsername(){ return username; } public double getLatitude(){ return latitude; } public double getLongitude(){ Page 2 of 24 return longitude; } public void setSessionid(int sessionid){ this.sessionid = sessionid; } public void setUsername(String usr){ this.username = usr; } public void setLatitude(double lat){ this.latitude = lat; } public void setLongitude(double lng){ this.longitude = lng; } } This code is a simple wrapper of the session information. There are two important aspects to show of this code: import com.google.gwt.user.client.rpc.IsSerializable This line is importing an interface called isSerializable from the Google Web Toolkit library. A serializable object is an object that can be transmitted and received by the client-side and server-side applications in the same form without the programmer’s intervention in coding and decoding it from its state to a byte stream and vice versa. public class TrackingUpdate implements IsSerializable This line is stating that the TrackingUpdate class is implementing the IsSerializable interface. Now, we need to create the service interface and the implementation of the interface. In order to do this, follow the next steps: 1. Right click in the package cse.usf.edu.lab5.client.services and in the menu click New->Java interface 2. Write down the name of the interface as TrackingServiceManager. Click finish 3. Select all the code that appears in the TrackingServiceManager.java screen and delete it 4. Copy and paste the following code in that area and save it (File->Save) Listing 2- TrackingServiceManager.java package cse.usf.edu.lab5.client.services; import com.google.gwt.user.client.rpc.RemoteService; import cse.usf.edu.lab5.client.entities.TrackingUpdate; Page 3 of 24 // An interface that exposes a method that returns the session updates. public interface TrackingServiceManager extends RemoteService{ public TrackingUpdate[] getTrackingUpdates(); } There are two important aspects to show of this code. The line, import com.google.gwt.user.client.rpc.RemoteService is importing an interface called RemoteService from the Google Web Toolkit library. This interface does not provide any methods, but its functionality is to state that the service interface that you wrote is going to be utilized by the Google Web Toolkit as a service provider. This is stated in the line public interface TrackingServiceManager extends RemoteService The next step is to write the service implementation. To do this, follow the next instructions: 1. Right click in the package cse.usf.edu.lab5.client.services and in the menu click New->Java class 2. Write down the name of the interface as TrackingServiceManagerImpl. Click finish 3. Select all the code that appears in the TrackingServiceManagerImpl.java screen and delete it 4. Copy and paste the following code in that area and save it (File->Save) Listing 3- TrackingServiceManagerImpl.java package cse.usf.edu.lab5.client.services; import com.google.gwt.user.server.rpc.RemoteServiceServlet; import cse.usf.edu.lab5.client.entities.TrackingUpdate; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; Page 4 of 24 import javax.naming.NamingException; import org.postgis.Point; // Provides server side implementation of the TrackingServiceManager interface public class TrackingServiceManagerImpl extends RemoteServiceServlet implements TrackingServiceManager{ public TrackingUpdate[] getTrackingUpdates() { Connection theConnection = null; try{ javax.naming.InitialContext ic = new javax.naming.InitialContext(); javax.sql.DataSource dataSource = (javax.sql.DataSource)ic.lookup("jdbc/lbsclass"); theConnection = dataSource.getConnection(); PreparedStatement queryStatement = theConnection.prepareStatement("select fieldsession.sessionid as sesid, fielduser.username as uname, ST_AsText(tracking.position) as pos "+ "from fieldsession, tracking,fielduser, (select max(idtracking) as idtrack "+ "from fieldsession, tracking "+ "where fieldsession.datestop is NULL and fieldsession.sessionid = tracking.sessionid "+"group by fieldsession.sessionid) as s2 "+"where fieldsession.datestop is NULL and "+"fieldsession.sessionid = tracking.sessionid and "+ "tracking.idtracking = s2.idtrack and "+"fieldsession.iduser = fielduser.iduser"); ResultSet rs = queryStatement.executeQuery(); List returnList = new LinkedList(); while(rs.next()) { TrackingUpdate newUpdate = new TrackingUpdate(); newUpdate.setSessionid(rs.getInt("sesid")); newUpdate.setUsername(rs.getString("uname")); Point theNewPoint = new Point(rs.getString("pos")); newUpdate.setLongitude(theNewPoint.getX()); newUpdate.setLatitude(theNewPoint.getY()); returnList.add(newUpdate); } theConnection.close(); if(!returnList.isEmpty()) { Page 5 of 24 TrackingUpdate theReturnVector[] = new TrackingUpdate[returnList.size()]; int i = 0; for (Iterator it = returnList.iterator(); it.hasNext();) { TrackingUpdate theUpdate = (TrackingUpdate) it.next(); theReturnVector[i] = theUpdate; i++; } return theReturnVector; } return null; } catch (NamingException ex){ Logger.getLogger(TrackingServiceManagerImpl.class.getName()).log(Level.SEVER E, null, ex); try { theConnection.close(); } catch (SQLException ex1) { Logger.getLogger(TrackingServiceManagerImpl.class.getName()).log(Level.SEVER E, null, ex1); } } catch (SQLException ex){ Logger.getLogger(TrackingServiceManagerImpl.class.getName()).log(Level.SEVER E, null, ex); } return null; } } In order to understand the code, read the following explanation carefully. There are many imports in this file. The first import that is written in the code is import com.google.gwt.user.server.rpc.RemoteServiceServlet; This import is required because it provides the GWT service functionality. GWT services are pieces of code that execute over servlets. In Java, servlets are classes that listen for HTTP requests in a URL and execute code in the server when a client invokes such URL. The import that comes after this, import cse.usf.edu.lab5.client.entities.TrackingUpdate; Page 6 of 24 is making available the class which objects are going to be returned to the client upon the service invocation. The next set of imports import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Iterator; import java.util.LinkedList; import java.util.List; correspond to the SQL libraries in Java and the list and linked list classes available in the standard J2SE API. The following set of imports import java.util.logging.Level; import java.util.logging.Logger; import javax.naming.NamingException; correspond to the management of the exceptions (errors) that can be raised when the query is performed in the database. Finally the last import import org.postgis.Point; provides a wrapper of the point object stored in the database. We will use these objects to translate the coordinates obtained from the query to latitude and longitude values. The basic idea of this code is to provide an implementation of the service interface for the TrackingManagerService. In order to do this, the TrackingManagerService.java class must provide an implementation of such interface. In Java, an implementation of an interface means to write code in a method whose name and parameters and return value correspond to the ones that are declared in the interface. Look that in the declaration of the class public class TrackingServiceManagerImpl extends RemoteServiceServlet implements TrackingServiceManager{ it states that the class is implementing the TrackingServiceManager interface. Remember from Listing 2 that the interface is only declaring one procedure public TrackingUpdate[] getTrackingUpdates(); This method is therefore the one that is implemented in the TrackingServiceManagerImpl class. In general, the implementation will query the database to get the location of those Page 7 of 24 sessions that are active in the system. Those sessions correspond to the sessions in the database whose date end is null. The code is basically divided into three sections: 1. Connecting to the database and execution of the query 2. Retrieving of the results of the query 3. Returning an array of TrackingUpdate objects with the information about sessions. 1. Connecting to the database and execution of the query For connecting to the database, the code uses a connection pool resource that is provided by the server where the application is deployed. The code that perform this is Connection theConnection = null; javax.naming.InitialContext ic = new javax.naming.InitialContext(); javax.sql.DataSource dataSource = javax.sql.DataSource)ic.lookup("jdbc/lbsclass"); theConnection = dataSource.getConnection(); The connection in the code above is obtained by using the jdbc/lbsclass resource from the database. This code looks for the resource (InitialContect ic = new …), get the data source and after this, it gets the connection. If the resource is not found, the code will throw a NamingException. Now that if the connection is obtained, the code will invoke the database with a query wrapped in a PreparedStatement object called queryStatement PreparedStatement queryStatement = theConnection.prepareStatement("select fieldsession.sessionid as sesid, fielduser.username as uname, ST_AsText(tracking.position) as pos "+ "from fieldsession, tracking,fielduser, (select max(idtracking) as idtrack "+ "from fieldsession, tracking "+ "where fieldsession.datestop is NULL and fieldsession.sessionid = tracking.sessionid "+ group by fieldsession.sessionid) as s2 "+ "where fieldsession.datestop is NULL and "+ "fieldsession.sessionid = tracking.sessionid and "+ "tracking.idtracking = s2.idtrack and "+"fieldsession.iduser = fielduser.iduser" ); ResultSet rs = queryStatement.executeQuery(); Page 8 of 24 This code is obtaining the last received location per active session in the system. In order to do this, the SQL query uses a subquery in the from (select max(idtracking) as idtrack from fieldsession, tracking where fieldsession.datestop is NULL and fieldsession.sessionid = tracking.sessionid group by fieldsession.sessionid) as s2 which provides the id of the last valid received location of any active session. Looking at this subquery, an active session is a session whose datestop is null. As this subquery is performed in the from part of the query, the results are seen by the database engine as a table. These results are then cross producted with the others tables (fieldsession, and tracking) in order to obtain the information we want, which is the sessionid, the username, and the location of each active session. The statement ResultSet rs = queryStatement.executeQuery(); is the one that performs the query to the database. 2. Retrieving of the results of the query As there is no way to know beforehand the number of records in the ResultSet to safely allocate the size of the returning TrackingUpdate array, we will use a list of TrackingUpdate objects as intermediate storage for obtaining the records from the ResultSet and know the size of the returning array. The code that provides this functionality is List returnList = new LinkedList(); while(rs.next()) { TrackingUpdate newUpdate = new TrackingUpdate(); newUpdate.setSessionid(rs.getInt("sesid")); newUpdate.setUsername(rs.getString("uname")); Point theNewPoint = new Point(rs.getString("pos")); newUpdate.setLongitude(theNewPoint.getX()); newUpdate.setLatitude(theNewPoint.getY()); returnList.add(newUpdate); } theConnection.close(); This code iterates over the ResultSet, creating a TrackingUpdate object per active session record in the database and adding each object to a list (returnList). From this code observe the lines Page 9 of 24 Point theNewPoint = new Point(rs.getString("pos")); newUpdate.setLongitude(theNewPoint.getX()); newUpdate.setLatitude(theNewPoint.getY()); As the location is obtained as a text string from the query, the org.postgis.Point object is providing the parsing from string to double values for the latitude and longitude. Once all the records have been placed in the list, we can know the size of the array. 3. Returning an array of TrackingUpdate objects with the information about sessions The last part of this code is the one that returns the array. The code which is shown below, builds an array of TrackingUpdates using the size of the list for its own size. After this, it basically iterates over the list, and copies the contents of the list to the array. If the list is empty, the procedure will return null. if(!returnList.isEmpty()) { TrackingUpdate theReturnVector[] = new TrackingUpdate[returnList.size()]; int i = 0; for (Iterator it = returnList.iterator(); it.hasNext();) { TrackingUpdate theUpdate = (TrackingUpdate) it.next(); theReturnVector[i] = theUpdate; i++; } return theReturnVector; } return null; In order to test the database, use the following username and password to connect to the lbsclass_lab5 database and set up your connection pool and jdbc resource in the Sun Java Application Server. Username: lbsclass Password: lab5 Set up a connection in the PgAdmin so you can run the “select” query statement from there. We have populated the database with some information. The result of the query should be: 424;”phoneuser4”;”POINT(-82.4563676118851 27.9414403438568)”813;”taclan0”;”POINT(-82.411315 28.0601866666667)”848;”ajperez4”;”POINT(-82.41472166666667 28.0598483333333)” Page 10 of 24 This corresponds to the locations and active sessions in the database that are used to show the locations in the map. The lbsclass_lab5 database is the one that you should use for your lab. Now that we have the implementation developed, the next step is to register the implementation with the Web application deployment descriptor. To do this, follow the next steps: 1. Go to the Project panel, click on the + sign of the label Configuration and double click over the web.xml file 2. A page that contains the configuration of the application appears on the right side. Click on the Servlets button and click the Add Servlet Element button 3. In the window, write GwtDeviceManager in the Name textfield. In the Servlet class click on the Browse button and look for the TrackingServiceManagerImpl.java class. In URL Pattern(s) write /services/TrackingManager. Click OK and click the File->Save 4. When deployed, the URL of the TrackingManager will be http://ip_machine:port/lab5/services/TrackingManager. This URL will be used by the client to invoke the service. At this point, the server side code can be deployed. To do this, go to the Projects panel, right click on the project, and choose the Clean and Build option. After the building process is completed, go again to the Projects panel, right click on the project, and choose the Deploy option. Now, we are ready to create the Eclipse GWT project. Experiment 2 Creating a GWT project in Eclipse and configuring it to use Google Maps In this part you will create the Eclipse GWT project to develop a Google Map application. To get started, do the following steps: 1. Go to the Windows Command line. (Start button -> Run, in the Open text box write cmd.exe and click OK 2. Change folder to the folder you have placed the Google Web Toolkit 3. Write the following commands projectCreator -eclipse Lab5 -out Lab5 Page 11 of 24 and then applicationCreator -eclipse Lab5 -out Lab5 -overwrite cse.usf.edu.lab5.client.Lab5 4. Open Eclipse, and select your workspace 5. Go to menu File->Import, under General select Existing projects into Workspace and click Next 6. Select the root directory which in our case is the folder where the GWT was installed. Click OK 7. Now check the option Copy projects into workspace and click Finish 8. Run the project, to do this right click on the project name in the Package Explorer (upper right side) Run As -> Run Configurations 9. On the left side of the window, click Java Application and select Lab5. Click Run 10. To close the application, close the Google Web Toolkit Development Shell Now we have a basic GWT project on which we will develop the visualization of the sessions. 11. Create a Google Maps key for the http://localhost:8888/ by signing with Google in the following URL http://code.google.com/apis/maps/signup.html 12. Add the Google Maps library to the project. To do this right click in the project name in the Package Explorer, then in the menu click Properties. Click in Java Build Path and in the right panel click in Libraries. Now click Add External JAR, search and select the gwt-maps.jar file. Click Open and OK. 13. Use the package explorer to find the Lab5.gwt.xml file. This file should be in the src->cse.usf.edu.lab5 path. Right click in the file and then in the menu click Open With->Text Editor. Under the line <inherits name=’com.google.gwt.user.User’/> write the following: <inherits name=’com.google.gwt.maps.GoogleMaps’/> Under the line <stylesheet src='Lab5.css' /> write the following line <script src="http://maps.google.com/maps?gwt=1&amp;file=api&amp;v=2&amp;key=yo ur_key"/> Replace the your_key with the key obtained in step 11 and save the changes to the file. Every application developed with GWT has a .gwt.xml file. This file can be seen as the application descriptor. It contains information that the GWT shell and compiler utilize to link external JavaScript libraries and start the application. Page 12 of 24 Experiment 3 Creating a visual interface that uses Google Maps to show the active sessions in the system 1. Create two packages in your Eclipse project named exactly the same as the ones of steps 12 and 13 of Experiment 1. To do this, right click in the project’s name in the Package Explorer, and in the menu click New->Package 2. Create a class as shown in Listing 1 under the package cse.usf.edu.lab5.client.entities. Copy the contents of Listing 1 to this class 3. Create an interface as shown in Listing 2 under the package cse.usf.edu.lab5.client.services. Copy the contents of Listing 2 to this interface 4. Under the package cse.usf.edu.lab5.client.services create an interface called TrackingServiceManagerAsync 5. Select all the code that appears in the TrackingServiceManager.java screen and delete it 6. Copy and paste the following code in that area and save it (File->Save) Listing 4- TrackingServiceManagerAsync.java package cse.usf.edu.lab5.client.services; import com.google.gwt.user.client.rpc.AsyncCallback; import cse.usf.edu.lab5.client.entities.TrackingUpdate; public interface TrackingServiceManagerAsync { public void getTrackingUpdates(AsyncCallback callback); } 7. Create a new class called TrackingWindow under the cse.usf.edu.lab5.client package. Erase all the code that appears in the TrackingWindow.java and replace it by the code shown in Listing 5 Listing 5- TrackingServiceManager.java package cse.usf.edu.lab5.client; import com.google.gwt.core.client.GWT; import com.google.gwt.user.client.*; import com.google.gwt.user.client.ui.*; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.rpc.ServiceDefTarget; Page 13 of 24 import com.google.gwt.maps.client.InfoWindowContent; import com.google.gwt.maps.client.MapWidget; import com.google.gwt.maps.client.control.LargeMapControl; import com.google.gwt.maps.client.control.MapTypeControl; import com.google.gwt.maps.client.event.MarkerClickHandler; import com.google.gwt.maps.client.geom.LatLng; import com.google.gwt.maps.client.overlay.Marker; import cse.usf.edu.lab5.client.entities.TrackingUpdate; import cse.usf.edu.lab5.client.services.TrackingServiceManager; import cse.usf.edu.lab5.client.services.TrackingServiceManagerAsync; public class TrackingWindow extends Composite { private AbsolutePanel backgroundPanel = null; private MapWidget theMapWidget = null; private Timer trackingTimer = null; public TrackingWindow() { super(); initializeComponents(); setLayout(); setProperties(); } protected void initializeComponents(){ backgroundPanel = new AbsolutePanel(); theMapWidget = new MapWidget(); backgroundPanel.add(theMapWidget, 1,1); updateActiveSessions(); trackingTimer = new Timer() { public void run() { updateActiveSessions(); } }; trackingTimer.scheduleRepeating(10000); } protected void setLayout(){ initWidget(backgroundPanel); } protected void setProperties(){ setHeight("600"); Page 14 of 24 setWidth("800"); backgroundPanel.setHeight("600"); backgroundPanel.setWidth("800"); theMapWidget.setHeight("600"); theMapWidget.setWidth("800"); theMapWidget.addControl(new LargeMapControl()); theMapWidget.addControl(new MapTypeControl()); } public void updateActiveSessions() { } } 8. Replace the updateActiveSessions() method of the above code with the code shown in Listing 6. Replace the String serviceURL = .... line with the following line: String serviceURL = http://ip_machine:port/lab5/services/TrackingManager; where ip_machine:port corresponds to the ip and HTTP port number where your server side code is deployed. 9. Open the Lab5.java class which can be found in the cse.usf.edu.lab5.client package. Modify the onModuleLoad() as shown in Listing 7. Save the code. Listing 6- The updateActiveSessions method public void updateActiveSessions() { TrackingServiceManagerAsync theTrackingManager = (TrackingServiceManagerAsync) GWT.create(TrackingServiceManager.class); ServiceDefTarget endpoint = (ServiceDefTarget) theTrackingManager; String remoteServiceURL = "http://localhost:8080/LbsBook/services/TrackingManager"; endpoint.setServiceEntryPoint(remoteServiceURL); AsyncCallback callback = new AsyncCallback() { public void onSuccess(Object result) { TrackingUpdate theUpdates[] = (TrackingUpdate[]) result; if(theUpdates != null) { theMapWidget.clearOverlays(); Page 15 of 24 for(int i = 0; i < theUpdates.length; i++) { final LatLng coordinates = LatLng.newInstance(theUpdates[i].getLatitude(), theUpdates[i].getLongitude()); final String theString = "Username: "+theUpdates[i].getUsername()+"<br>Session id:"+theUpdates[i].getsessionId(); Marker theNewMarker = new Marker(coordinates); MarkerClickHandler theHandler = new MarkerClickHandler(){ public void onClick(MarkerClickEvent event) { theMapWidget.getInfoWindow().open(coordinates, new InfoWindowContent(theString)); } }; theNewMarker.addMarkerClickHandler(theHandler); theMapWidget.addOverlay(theNewMarker); } } } public void onFailure(Throwable caught) { Window.alert("An Internal Error has ocurred: " + caught.getMessage()); trackingTimer.cancel(); } }; theTrackingManager.getTrackingUpdates(callback); } Listing 7- The onModuleLoad method in Lab5.java class public void onModuleLoad() { VerticalPanel vPanel = new VerticalPanel(); vPanel.setWidth("100%"); vPanel.setHorizontalAlignment(VerticalPanel.ALIGN_CENTER); RootPanel.get().add(vPanel); TrackingWindow theWindow = new TrackingWindow(); final DialogBox dialogBox = new DialogBox(); Page 16 of 24 dialogBox.setText("Map Example"); dialogBox.setAnimationEnabled(true); dialogBox.add(theWindow); dialogBox.center(); dialogBox.show(); } In order to understand the code, read the following explanation carefully. First take a look at the TrackingWindow.java file. There are many imports in this file. The first import that is written in the code is import com.google.gwt.core.client.GWT; This line is importing core functionalities from the framework, utilized for creating/loading classes in runtime. This is used in the code for creating the proxy that will perform the RPC (Remote Procedure Call). The next set of imports are needed for the user interface import com.google.gwt.user.client.*; import com.google.gwt.user.client.ui.*; The following imports are needed for the classes that perform the remote procedure call to the server (to get the session location updates). import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.rpc.ServiceDefTarget; ; Now this set of imports is needed to utilize the Google Maps classes. The first import in the set is the MapWidget. This class represents the Google Maps. The second and third (LargeMapControl and MapTypeControl) provides the map with the controllers to scroll the map and zoom to it and choose the map type (satellite, physical, etc...). The next two (MarkerClickHandler and InfoWindowContent) are needed as we will display the session information in a box when a balloon that represents the location of a session is clicked. Finally the last two imports of this set (the LatLng and the Marker) are used for the markers that are shown in the map and the location of such markers. import com.google.gwt.maps.client.MapWidget; import com.google.gwt.maps.client.control.LargeMapControl; import com.google.gwt.maps.client.control.MapTypeControl; import com.google.gwt.maps.client.event.MarkerClickHandler; import com.google.gwt.maps.client.InfoWindowContent; import com.google.gwt.maps.client.geom.LatLng; import com.google.gwt.maps.client.overlay.Marker; Page 17 of 24 Finally, the last set of imports for this class are import cse.usf.edu.lab5.client.entities.TrackingUpdate; import cse.usf.edu.lab5.client.services.TrackingServiceManager; import cse.usf.edu.lab5.client.services.TrackingServiceManagerAsync; These correspond to the class that abstracts the session information (TrackingUpdate) and the remote service interfaces. The main idea with this class is to provide a user interface component that every ten seconds retrieves updates about the sessions in the system and show such information in a map. In GWT those own UI components can be developed by extending the Composite class. The Composite class in GWT is provided to wrap user components in a single window (similar to the Jframe class in J2SE). Look that we do this on this line: public class TrackingWindow extends Composite The TrackingWindow class has three private variables, these are: private AbsolutePanel backgroundPanel = null; private MapWidget theMapWidget = null; private Timer trackingTimer = null; The first variable (backgroundPanel) is utilized as the base panel where the components of the TrackingWindow will be added. The second (theMapWidget) is the instance of the Google Maps where the locations will be shown. Finally, the third variable (trackingTimer) provides us with the functionality to invoke the server every ten seconds. The TrackingWindow class is made of 5 methods: public TrackingWindow(): Constructor of the class. initializeComponents, setLayout, and setProperties methods. This method invokes the protected void initializeComponents(): Initializes the three variables. This method is also utilized to setup the timer to invoke the update of the session information every 10 seconds. protected void setLayout(): Initializes the background panel. protected void setProperties(): Set the sizes for the background panel and for the MapWidget. public void updateActiveSessions(): Invokes the remote server procedure to update the session information. This method is the one that the timer invokes every 10 seconds. From these methods, we will explain the updateActiveSessions and the initializeComponents method as these are the two most important methods of this class. Page 18 of 24 As it has been stated before, the updateActiveSessions performs the invocation of the remote procedure in the server. In order to do this, the method performs the following steps: Proxy creation: The proxy is created by the factory method GWT.create() in the line TrackingServiceManagerAsync theTrackingManager = (TrackingServiceManagerAsync) GWT.create(TrackingServiceManager.class); using the service interface and returning an object that implements the asynchronous service interface. The proxy transmits the objects using HTTP, hiding the details from the programmers. Remote service location set up: A remote service location is set up in the lines ServiceDefTarget endpoint = (ServiceDefTarget) theTrackingManager; String remoteServiceURL = "http://localhost:8080/LbsBook/services/TrackingManager"; endpoint.setServiceEntryPoint(remoteServiceURL); where the URL of the Web object that implements the RPC service is located. Manager of the response set up: A response manager is set up between the lines AsyncCallback callback = new AsyncCallback() { public void onSuccess(Object result) { .. } public void onFailure(Throwable caught) { ... } }; where the callback object is created. AsyncCallback is an interface that implements two methods. The first, which is onSuccess(), is executed when the RPC is completed with success. The other, onFailure(), is executed if there is a communication failure with the server. Here we are creating an anonymous class that implements the AsyncCallback interface. In our code, the onSuccess method will be adding the session information to the Google Map. The onFailure() method will show a dialog box showing that something went wrong when performing the invocation. Objects preparation and RPC service invocation: This is done on the line theTrackingManager.getTrackingUpdates(callback); which is the line that actually performs the RPC call. In our example, there are no parameters for the invocation of the RPC. A question that you might be asking now is Page 19 of 24 why the AsyncCallback object is used to manage the response of the communication. The answer for this is as programs in JavaScript are single threaded, if there is a blocking call in the code, your JavaScript application freezes until the code returns. In this perspective, when you do asynchronous calls, you are avoiding the blocking of RPC calls, and therefore managing the invocation as events: one event that executes when the invocation was performed successfully, the other when it is not successful. Now take a look on the onSuccess of the AsyncCallback object public void onSuccess(Object result) { TrackingUpdate theUpdates[] = (TrackingUpdate[]) result; if(theUpdates != null) { theMapWidget.clearOverlays(); for(int i = 0; i < theUpdates.length; i++) { final LatLng coordinates = LatLng.newInstance(theUpdates[i].getLatitude(), theUpdates[i].getLongitude()); final String theString = "Username: "+theUpdates[i].getUsername()+"<br>Session id:"+theUpdates[i].getsessionId(); Marker theNewMarker = new Marker(coordinates); MarkerClickHandler theHandler = new MarkerClickHandler(){ public void onClick(MarkerClickEvent event) { theMapWidget.getInfoWindow().open(coordinates, new InfoWindowContent(theString)); } }; theNewMarker.addMarkerClickHandler(theHandler); theMapWidget.addOverlay(theNewMarker); } } } The first line TrackingUpdate theUpdates[] = (TrackingUpdate[]) result; is performing a casting on result parameter of the onSuccess call. In Java, casting means the interpretation of an object as another one. To do this, in runtime the system checks the types for both the object you are casting and the variable that you cast to. If the types do not correspond, then it throws a runtime exception. In this line, we are casting the Object Result to a vector of TrackingUpdate objects. Page 20 of 24 Now the following lines are adding a balloon in the MapWidget object per active session in the system. theMapWidget.clearOverlays(); for(int i = 0; i < theUpdates.length; i++) { final String theString = "Username: "+theUpdates[i].getUsername()+"<br>Session id:"+theUpdates[i].getsessionId(); Marker theNewMarker = new Marker(coordinates); MarkerClickHandler theHandler = new MarkerClickHandler(){ public void onClick(MarkerClickEvent event) { theMapWidget.getInfoWindow().open(coordinates, new InfoWindowContent(theString)); } }; theNewMarker.addMarkerClickHandler(theHandler); theMapWidget.addOverlay(theNewMarker); } The first line theMapWidget.clearOverlays(); is cleaning all the overlays in the map. After this, the code iterates over the array of TrackingUpdates and for every session it performs the following steps: Creation of a Map coordinates: This is performed in the line coordinates = theUpdates[i].getLongitude()); final LatLng LatLng.newInstance(theUpdates[i].getLatitude(), Creation of a marker based on the coordinates: Marker theNewMarker = new Marker(coordinates); Creation a click handler for the marker: Upon the click the marker, this code will display a window with information about the session MarkerClickHandler theHandler = new MarkerClickHandler(){ public void onClick(MarkerClickEvent event) { theMapWidget.getInfoWindow().open(coordinates, new InfoWindowContent(theString)); } }; theNewMarker.addMarkerClickHandler(theHandler); Page 21 of 24 Adding the overlay to the map. This is performed in the line theMapWidget.addOverlay(theNewMarker); which finally adds the marker to the map. Now go to the initalizeComponents() method of the TrackingWindow class. The important part of this code is the one that uses a timer to invoke the code that updates the session information trackingTimer = new Timer() { public void run() { updateActiveSessions(); } }; trackingTimer.scheduleRepeating(10000); All code that is within the run() method will be executed every certain amount of time, in this case, such time is setup to be 10 seconds. This is a way to emulate threads in GWT, however there is no thread concept in GWT, as all the applications in JavaScript are single threaded. The final explanation of this lab guide is how to initialize (entry point) a GWT application. Go to the Lab5.java class. You should have the code shown in Listing 8. Listing 8- The Lab5.java class package cse.usf.edu.lab5.client; import com.google.gwt.core.client.EntryPoint; import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.ClickListener; import com.google.gwt.user.client.ui.DialogBox; import com.google.gwt.user.client.ui.Image; import com.google.gwt.user.client.ui.RootPanel; import com.google.gwt.user.client.ui.VerticalPanel; import com.google.gwt.user.client.ui.Widget; /** * Entry point classes define <code>onModuleLoad()</code>. */ public class Lab5 implements EntryPoint { Page 22 of 24 /** * This is the entry point method. */ public void onModuleLoad() { VerticalPanel vPanel = new VerticalPanel(); vPanel.setWidth("100%"); vPanel.setHorizontalAlignment(VerticalPanel.ALIGN_CENTER); RootPanel.get().add(vPanel); TrackingWindow theWindow = new TrackingWindow(); final DialogBox dialogBox = new DialogBox(); dialogBox.setText("Map Example"); dialogBox.setAnimationEnabled(true); dialogBox.add(theWindow); dialogBox.center(); dialogBox.show(); } } In GWT an entry point of a GWT application is defined as a class that implements the EntryPoint interface. This interface declares the onModuleLoad() method that will be utilized as the first code that the application will execute when initialized. In this case, this code is declaring a panel where a DialogBox is added to it, and in the dialog box we are adding an instance of the TrackingWindow application. The entry point of the application is referenced in the Lab5.gwt.xml file. Look for this file on your project tree and you will find a line that says: <entry-point class='cse.usf.edu.lab5.client.Lab5'/> This line is configuring you application to declare what will be the entry point, in this case, this is the cse.usf.edu.lab5.client.Lab5 class. Now that the client and server codes have been developed, it is time to compile the application with GWT and deploy it along with the Web Project. The following steps indicate how to accomplish this: 1. Compile the Eclipse project with GWT: Run the Eclipse project and click on the Compile/Browse button 2. Copy the compiled files to the Web project application: Using the Windows Explorer, go to the Eclipse's GWT project location and copy the folder www to the Web folder of the NetBeans project 3. Build and Deploy: Using NetBeans, build and deploy the Web project Page 23 of 24 After the Web application has been deployed, it can be executed from a Web browser. In our case the URL to use would be http://ip_machine:port/lab5/services/TrackingManager; which consists of the path where the application resides in the server with IP address ip_machine:port. Experiment 5 Modify the TrackingWindow in such way that when the user clicks on the map, it not only shows the marker but also shows a table with the distance in meters from the coordinate that corresponds to the click to each of the active sessions. You must show in a text field the location of the click (in lat, long format) and in the table the user, the location (in lat, long format), and the distance. Hint: use the FlexTable component for the table and look in the gwt-maps javadocs for the click map listeners for the MapWidget. Report Turn in your software and a written report including: 1. Name of group members 2. Date of the project’s submission 3. Names of all project folders submitted 4. A brief set of instructions for using your application 5. Screen shots of your application, as appropriate 6. A brief summary of the activities and description of any problems you encountered Page 24 of 24