jMaki is all about enabling Java developers to use JavaScript in their Java based applications as either a JSP tag library or a JSF component. jMaki uses the best parts of Java and the best parts of JavaScript to deliver a rich Ajax style widgets. jMaki currently provides bootstrap widgets for many components from Dojo, Script.aculo.us, Yahoo UI Widgets, Spry, DHTML Goodies, and Google. jMaki provides a common interfaces to these widget libraries and allows you to use these libraries together in the same page.
This hands-on lab takes you through the basics of using jMaki
widgets for
developing highly responsive and interactive
AJAX-based web applications. This document is designed to get you going
as quickly as possible.
Expected duration: 100 minutes
This hands-on lab assumes you have some basic knowledge of, or programming experience with, the following technologies.
Before you begin, you need to install the following software on your
computer. Please note that this lab requires NetBeans IDE
5.5. If
you have only NetBeans IDE 5.0 on your computer, you will have to
install NetBeans 5.5. You don't have to uninstall NetBeans
IDE 5.0 before you install NetBeans IDE 5.5 -
NetBeans IDE 5.0 and NetBeans IDE 5.5 coexist on a single computer
without a
problem.
In this exercise, you are going to learn
how to use Yahoo Data Table widget and Block List widget to display the
data from an external RSS feed. You are also going to learn how
to modify the Block List widget so that it displays with additonal data
such as date information and with a different style, for example,
different font size. Usage of external service is enabled through
XmlHttpProxy feature of jMaki technology.
This exercise is based on Arun Gupta's Creating Mashups with jMaki - Display RSS feed in jMaki Widgets screencast blog.
0. Start NetBeans 5.5 IDE (if you have
not done so yet)
1. Create a new NetBeans project








|
<a:widget name="yahoo.dataTable" service="xhp?id=rss" /> |



| <%@ taglib prefix="a"
uri="http://jmaki/v1.0/jsp" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html> <head> <link rel="stylesheet" href="jmaki-2column-footer.css" type="text/css"></link> <title>Page Title</title> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> </head> <body> <div class="border"> <div class="header"> <div class="banner">Application Name</div> <div class="subheader"> <div> <a href="mailto:feedback@youraddress">Feedback</a> | <a href="#">Site Map</a> | <a href="#">Home</a> </div> </div> <!-- sub-header --> </div> <!-- header --> <div class="main"> <div class="rightColumn" style="height:400px"> <a:widget name="yahoo.dataTable" service="xhp?id=rss&count=3" /> </div> <!-- end leftColumn --> <div class="leftColumn" style="height:400px"> Left Column </div> <!-- leftColumn --> </div> <!-- main --> <div class="footer">Footer</div> </div> <!-- border --> </body> </html> |



| <%@ taglib prefix="a"
uri="http://jmaki/v1.0/jsp" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html> <head> <link rel="stylesheet" href="jmaki-2column-footer.css" type="text/css"></link> <title>Page Title</title> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> </head> <body> <div class="border"> <div class="header"> <div class="banner">Application Name</div> <div class="subheader"> <div> <a href="mailto:feedback@youraddress">Feedback</a> | <a href="#">Site Map</a> | <a href="#">Home</a> </div> </div> <!-- sub-header --> </div> <!-- header --> <div class="main"> <div class="rightColumn" style="height:400px"> <a:widget name="yahoo.dataTable" service="xhp?id=rss&count=3" /> </div> <!-- end leftColumn --> <div class="leftColumn" style="height:400px"> <a:widget name="jmaki.blockList" service="xhp?id=rss&count=3" /> </div> <!-- leftColumn --> </div> <!-- main --> <div class="footer">Footer</div> </div> <!-- border --> </body> </html> |


| <div id="${uuid}"
class="jmakiStack jmakiBackround"> <div id="${uuid}_content" class="jmakiStackContent"> <ul id="${uuid}_list"> </ul> </div> </div> <div id="${uuid}_nav" class="jmakiStackNav"> <div id="${uuid}_previous" class="previous">Previoius</div> <div id="${uuid}_mid" class="mid"></div> <div id="${uuid}_next" class="next">Next</div> </div> <div id="${uuid}_template" style="display:none" class="jmakiStackItem"> <a href="@{link}">@{title}</a><br/> <br/>@{date}<br/> @{description}<br/> </div> |


| <div id="${uuid}"
class="jmakiStack jmakiBackround"> <div id="${uuid}_content" class="jmakiStackContent"> <ul id="${uuid}_list"> </ul> </div> </div> <div id="${uuid}_nav" class="jmakiStackNav"> <div id="${uuid}_previous" class="previous">Previoius</div> <div id="${uuid}_mid" class="mid"></div> <div id="${uuid}_next" class="next">Next</div> </div> <div id="${uuid}_template" style="display:none" class="jmakiStackItem"> <a href="@{link}" style="font-size: 16pt">@{title}</a><br/> <br/>@{date}<br/> @{description}<br/> </div> |


In this exercise, you learned how to use
Data Table widget and Block List widget. You learned how to
change these two widgets so that they use the external services.
Return to the top
In this exercise, you are going to learn
how to use Yahoo Map, Google Map, Yahoo GeoCoder widgets to build a
simple yet practical application in which Yhaoo Map and Google Map
widgets display the map information that is specified in the Yahoo
GeoCoder widget by the user. This feature is based on the
"Publish and Subscribe" event model of jMaki technology.
This exercise is based on Arun Gupta's
Creating
Mashups with jMaki - Mashing up the Maps screencast blog.
Another blog, Fun
with jMaki:Using the Yahoo Geocoder service with the Dojo Combobox,
contains some explanation on how things work.
1. Create a new jMaki NetBeans project

Figure-2.11: Name and Location pane

2. Select jMaki Ajax Framework.

Figure-2.13: Select jMaki Ajax Framework



| <%@ taglib prefix="a"
uri="http://jmaki/v1.0/jsp" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html> <head> <link rel="stylesheet" href="jmaki-2column-footer.css" type="text/css"></link> <title>Page Title</title> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> </head> <body> <div class="border"> <div class="header"> <div class="banner">Application Name</div> <div class="subheader"> <div> <a href="mailto:feedback@youraddress">Feedback</a> | <a href="#">Site Map</a> | <a href="#">Home</a> </div> </div> <!-- sub-header --> </div> <!-- header --> <div class="main"> <div class="rightColumn" style="height:400px"> <a:widget name="google.map" args="{ centerLat : 37.4041960114344, centerLon : -122.008194923401 }" /> </div> <!-- end leftColumn --> <div class="leftColumn" style="height:400px"> <a:widget name="yahoo.map" args="{ centerLat : 37.4041960114344, centerLon : -122.008194923401 }" /> </div> <!-- leftColumn --> </div> <!-- main --> <div class="footer"><a:widget name="yahoo.geocoder" /> </div> </div> <!-- border --> </body> </html> |
| <div id="${uuid}"> <form onsubmit="jmaki.attributes.get('${uuid}').getCoordinates();return false;"> <b>Location:</b> <input type="text" id="${uuid}_location"> <input type="button" value="Get Coordinates" onclick="jmaki.attributes.get('${uuid}').getCoordinates();"> </form> </div> |
| // define the namespaces jmaki.namespace("jmaki.widgets.yahoo.geocoder"); /** * Yahoo Geocoder Widget * This widget lets you find geocordinates using the XMLHttpProxy using * the Yahoo Geocoder service * * @author Greg Murray * (original author) * @author Ahmad M. Zawawi <ahmad.zawawi@gmail.com> * (Updated to new yahoo jmaki widget standard) * * @see http://developer.yahoo.com/maps/rest/V1/geocode.html */ jmaki.widgets.yahoo.geocoder.Widget = function(wargs) { var topic = "/yahoo/geocoder"; var uuid = wargs.uuid; var self = this; // we run on the xhp now var service = jmaki.xhp; //overide topic name if needed if (wargs.args && wargs.args.topic) { topic = wargs.args.topic; } var location; /** */ this.getCoordinates = function() { location = encodeURIComponent(document.getElementById(uuid + "_location").value); var encodedLocation = encodeURIComponent("location=" + location); var url = service + "?id=yahoogeocoder&urlparams=" + encodedLocation; jmaki.doAjax({url: url, callback: function(req) { var _req=req; postProcess(_req);}}); } /** */ function postProcess(req) { if (req.readyState == 4) { if (req.status == 200) { var v = {success:false}; if(req.responseText.length > 0) { var response = eval("(" + req.responseText + ")"); var coordinates = response.coordinates; v = {success:true,results:coordinates}; //for compatibility (deprecated): we leave this one for a while jmaki.publish(topic, coordinates); } //the new format is here (as in v) //with status flag sent jmaki.publish(topic + "/onGeocode", {id: uuid, value:v} ) } } } } |
| jmaki.namespace("jmaki.listeners"); jmaki.listeners.geocoderListener = function(coordinates) { var keys = jmaki.attributes.keys(); // scan the widgets for all yahoo maps for (var l = 0; l < keys.length; l++) { if (jmaki.widgets.yahoo && jmaki.widgets.yahoo.map && jmaki.widgets.yahoo.map.Widget && jmaki.attributes.get(keys[l]) instanceof jmaki.widgets.yahoo.map.Widget) { var _map = jmaki.attributes.get(keys[l]).map; var centerPoint = new YGeoPoint(coordinates[0].latitude,coordinates[0].longitude); var marker = new YMarker(centerPoint); var txt = '<div style="width:160px;height:50px;"><b>' + coordinates[0].address + ' ' + coordinates[0].city + ' ' + coordinates[0].state + '</b></div>'; marker.addAutoExpand(txt); _map.addOverlay(marker); _map.drawZoomAndCenter(centerPoint); } else if (typeof GLatLng != 'undefined' && jmaki.widgets.google && jmaki.widgets.google.map && jmaki.widgets.google.map.Widget && jmaki.attributes.get(keys[l]) instanceof jmaki.widgets.google.map.Widget) { // set the google map var _map = jmaki.attributes.get(keys[l]).map; var centerPoint = new GLatLng(coordinates[0].latitude,coordinates[0].longitude); _map.setCenter(centerPoint); var marker = new GMarker(centerPoint); _map.addOverlay(marker); var txt = '<div style="width:160px;height:50px;"><b>' + coordinates[0].address + ' ' + coordinates[0].city + ' ' + coordinates[0].state + '</b></div>'; marker.openInfoWindowHtml(txt); } } } // add listerner mapping for geocoder jmaki.addGlueListener(new RegExp("/jmaki/plotmap$"), "jmaki.listeners.geocoderListener"); // add listerner mapping for backward compatibility jmaki.addGlueListener(new RegExp("/yahoo/geocoder$"), "jmaki.listeners.geocoderListener"); // sytem level filters jmaki.namespace("jmaki.filters"); // convert an rss feed to the jMaki table format jmaki.filters.tableFilter = function(input) { var _columns = [ {title: 'Title'}, //{title: 'URL'}, {title: 'Date'}, {title: 'Description'} ]; var _rows = []; for (var _i=0; _i < input.channel.items.length;_i++) { var row = [ input.channel.items[_i].title, // input.channel.items[_i].link, input.channel.items[_i].date, input.channel.items[_i].description ]; _rows.push(row); } return {columns : _columns, rows : _rows}; } // convert an rss feed to the jMaki accordion format jmaki.filters.accordionFilter = function(input) { var _rows = []; for (var _i=0; _i < input.channel.items.length;_i++) { var row = { label : input.channel.items[_i].title, // input.channel.items[_i].link, // input.channel.items[_i].date, content : input.channel.items[_i].description } _rows.push(row); } jmaki.log("rows count=" + _rows.length); return {rows : _rows}; } |
In this exercise, you have built
and run a couple of sample applications using various jMaki widgets
that are
available from jMaki NetBeans plug-in. You also tried different
CSS layouts.
In this exercise, you are going to build a mashup application in
which Craig's list and Yahoo map is mashed up.



| <%@ taglib prefix="a"
uri="http://jmaki/v1.0/jsp" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <link rel="stylesheet" href="jmaki-2column-footer.css" type="text/css"></link> <html> <head> <title>jMaki CraigsList Mashup Sample</title> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> <style type="text/css"> .logo { background: url(images/jmaki-seal.png) top right;width:100px; height:80px;margin-top:0px;background-repeat: no-repeat } </style> <!--[if lt IE 7]> <style type="text/css"> .logo { background: none; filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true, sizingMethod=image src=images/jmaki-seal.png);cursor:pointer } </style> <![endif]--> </head> <body> <div class="border"> <div class="header"> <div class="banner"><div class="logo" style="float:left"></div> <div style="margin-top:20px; float:left;">jMaki CraigsList Mashup Sample</div> </div> <div class="subheader"> <div> <a href="mailto:feedback@youraddress">Feedback</a> | <a href="#">Site Map</a> | <a href="#">Home</a> </div> </div> <!-- sub-header --> </div> <!-- header --> <div class="main"> <div class="rightColumn" style="height:400px"> <a:widget name="yahoo.map" args="{ centerLat : 37.4041960114344, centerLon : -122.008194923401 }" /> </div> <!-- end leftColumn --> <div class="leftColumn" style="height:400px"> <a:widget name="dojo.table" args="{filter : 'jmaki.filters.clTableFilter'}" service="/xhp?key=cl" /> </div> <!-- leftColumn --> </div> <!-- main --> <div class="footer">Data Provided by <a href="http://www.craigslist.org">Craigs List</a>, Maps Provided by <a href="http://www.yahoo.com">Yahoo</a> / <a href="http://www.google.com">Google</a>, Geocoder by <a href="http://www.yahoo.com">Yahoo</a>, and Filter Table provided by the <a href="http://www.dojotoolkit.org">Dojo Toolkit<a/>.</div> </div> <!-- border --> </body> </html> |
| {"xhp": { "version": "1.1", "services": [ {"id": "cl", "url":"http://sfbay.craigslist.org/sby/rfs/index.rss", "xslStyleSheet": "rss.xsl" }, {"id": "yahoogeocoder", "url":"http://api.local.yahoo.com/MapsService/V1/geocode", "apikey" : "appid=jmaki-key", "xslStyleSheet": "yahoo-geocoder.xsl", "defaultURLParams": "location=santa+clara,+ca" } ] } } |
| jmaki.namespace("jmaki.listeners"); jmaki.listeners.mapLocation = function(loc) { var location = "location="+ loc + ",ca"; dojo.io.bind({ url: jmaki.xhp + "?key=yahoogeocoder&urlparams=" + encodeURIComponent(location), method: "get", mimetype: "text/json", load : function(type, data) { jmaki.publish("/jmaki/plotmap", data.coordinates); } }); } // add listerner mapping for geocoder jmaki.addGlueListener("/jmaki/mapLocation", "jmaki.listeners.mapLocation"); jmaki.namespace("jmaki.filters"); // convert an rss feed to the jMaki table format jmaki.filters.clTableFilter = function(input) { var _columns = [ {title: 'Title'}, {title: 'City'}, {title: 'Price'} ]; var _rows = []; // parse each title to get the name, city, and price // title contains description (city / district | neighborhood) $00000 for (var _i=0; _i < input.channel.items.length;_i++) { var _link = input.channel.items[_i].link; var _title = input.channel.items[_i].title; var _name = ""; var cityStart = _title.lastIndexOf("("); if (cityStart != -1) _name = _title.substring(0, cityStart); var cityEnd = _title.indexOf(")", cityStart); var _city = ""; if (cityStart != -1 && cityEnd != -1) { _city = _title.substring(cityStart + 1, cityEnd); // get rid of downtown north south east and west _city = _city.replace("downtown", "").replace("north", ""); _city = _city.replace("south", "").replace("east", ""); _city = _city.replace("west", ""); // only go for the city when district is mentioned if (_city.indexOf("/") != -1) _city = _city.substring(0, _city.indexOf("/") -1); } // create a link that will publish and event to map the city var _cityLink = "<a href=\"javascript:jmaki.publish('/jmaki/mapLocation','" + _city + "');\">" + _city + "</a>"; var _nameLink = "<a href=\"" + _link + "\" target=\"external\">" + _name + "</a>"; var _date = input.channel.items[_i].date; var _price = /\$[0-9]*/.exec(_title); var row = [ _nameLink, _cityLink, _price ]; _rows.push(row); } return {columns : _columns, rows : _rows}; } |
This exercise is based on Arun Gupta's Creating Mashups with jMaki - A real-life RIA using jMaki (Sun Tech Days Event Map) screencast blog.







| create table TECHDAYS_SCHEDULE
(startDate date, endDate date, location varchar(255), PRIMARY KEY (startDate, endDate)) INSERT INTO TECHDAYS_SCHEDULE VALUES ('9/11/2007', '9/12/2007', 'Boston, United States'); INSERT INTO TECHDAYS_SCHEDULE VALUES ('9/24/2007', '9/25/2007', 'Rome, Italy'); INSERT INTO TECHDAYS_SCHEDULE VALUES ('9/26/2007', '9/28/2007', 'Milan, Italy'); INSERT INTO TECHDAYS_SCHEDULE VALUES ('10/19/2007', '10/19/2007', 'Taipei, Taiwan'); INSERT INTO TECHDAYS_SCHEDULE VALUES ('10/23/2007', '10/25/2007', 'Shanghai, China'); INSERT INTO TECHDAYS_SCHEDULE VALUES ('11/1/2007', '11/3/2007', 'Beijing, China'); INSERT INTO TECHDAYS_SCHEDULE VALUES ('11/6/2007', '11/8/2007', 'Tokyo, Japan'); INSERT INTO TECHDAYS_SCHEDULE VALUES ('12/3/2007', '12/5/2007', 'Frankfurt, Germany'); INSERT INTO TECHDAYS_SCHEDULE VALUES ('1/9/2008', '1/10/2008', 'Atlanta, United States'); INSERT INTO TECHDAYS_SCHEDULE VALUES ('2/27/2008', '2/29/2008', 'Bangalore, India'); INSERT INTO TECHDAYS_SCHEDULE VALUES ('3/4/2008', '3/6/2008', 'Sydney, Australia'); INSERT INTO TECHDAYS_SCHEDULE VALUES ('3/11/2008', '3/13/2008', 'Johannesburg, South Africa'); INSERT INTO TECHDAYS_SCHEDULE VALUES ('4/1/2008', '4/1/2008', 'St Petersburg, Russia'); INSERT INTO TECHDAYS_SCHEDULE VALUES ('4/1/2008', '5/1/2008', 'Manila, Philippines'); INSERT INTO TECHDAYS_SCHEDULE VALUES ('5/21/2008', '5/23/2008', 'Mexico City, Mexico'); |







| @NamedQueries({@NamedQuery(name =
"TechdaysSchedule.findAll", query = "SELECT t FROM TechdaysSchedule t"), |



| <%@ page import="java.util.*"
%> <%@ page import="suntechdays.TechdaysSchedule" %> <%@ page import="javax.persistence.*" %> <% EntityManagerFactory emf = Persistence.createEntityManagerFactory("jmaki-suntechdaysPU"); EntityManager em = emf.createEntityManager(); List list = em.createNamedQuery("TechdaysSchedule.findAll").getResultList(); out.println("{columns : [" + "{ label : 'City', id : 'city'}," + "{ label :'Start Date', id : 'startdate'}," + "{ label :'End Date', id : 'enddate'}" + "],"); out.println("rows: ["); for (int i = 0; i < list.size() - 1; i++) { TechdaysSchedule techdays = (TechdaysSchedule) list.get(i); out.print("{ id: '" + techdays.getLocation() + "', " + "city: '" + techdays.getLocation() + "'," + "startdate: '" + techdays.getTechdaysSchedulePK().getStartdate() + "'," + "enddate: '" + techdays.getTechdaysSchedulePK().getEnddate() + "'}"); if (i < list.size() - 1) { out.println(","); } else { out.println(); } } out.println("] }"); %> |




In this exercise, you learned how to use JPA as a backend service.
This exercise is based on Arun Gupta's Creating Mashups with jMaki - A real-life RIA using jMaki (Sun Tech Days Event Map) screencast blog.
| @NamedQuery(name =
"TechdaysSchedule.findByMonth",query = "SELECT t FROM TechdaysSchedule
t WHERE t.techdaysSchedulePK.startdate >= :firstdate and
t.techdaysSchedulePK.enddate <= :lastdate"), |
| <%@ page import="java.util.*"
%> <%@ page import="suntechdays.*" %> <%@ page import="javax.persistence.*" %> <%@ page import="java.text.SimpleDateFormat" %> <% String dateParam = request.getParameter("date"); Date date = null; if (dateParam == null || "".equals(dateParam)) { date = Calendar.getInstance().getTime(); } else { SimpleDateFormat sdf = new SimpleDateFormat("MMM/dd/yyyy"); date = sdf.parse(dateParam); } EntityManagerFactory emf = Persistence.createEntityManagerFactory("jmaki-suntechdaysmapPU"); EntityManager em = emf.createEntityManager(); Calendar cal = Calendar.getInstance(); cal.setTime(date); cal.set(Calendar.DATE, 1); Date firstDateOfMonth = cal.getTime(); cal.set(Calendar.DATE, cal.getActualMaximum(Calendar.DAY_OF_MONTH)); Date lastDateOfMonth = cal.getTime(); List<TechdaysSchedule> list = em.createNamedQuery("TechdaysSchedule.findByMonth"). setParameter("firstdate", firstDateOfMonth). setParameter("lastdate", lastDateOfMonth). getResultList(); out.println("["); boolean first = true; int count = 0; SimpleDateFormat sdf = new SimpleDateFormat("MMM d, yyyy"); for (TechdaysSchedule t : list) { StringBuffer buf = new StringBuffer(); buf.append(sdf.format(t.getTechdaysSchedulePK().getStartdate())); buf.append(" - "); buf.append(sdf.format(t.getTechdaysSchedulePK().getEnddate())); String city = t.getLocation() + ", (" + buf.toString() + ")"; out.println("{"); out.println("name: \"" + t.getLocation() + "\", "); out.println("label: \"" + city + "\", "); out.println("value: \"" + city + "\""); if (first) { out.println(", selected: " + true); first = false; } out.println("}"); if (count++ < list.size() - 1) { out.println(","); } } out.println("]"); %> |
In this exercise, you learned how to use JPA as a backend service.