Client applications with Ajax Solr: JavaScript vs. servlets
Join the DZone community and get the full member experience.
Join For FreeAre we sure that Java servlets are the most productive means to deliver a web interface? Let's dig through a client application written in JavaScript and find out.
Ajax Solr
Ajax Solr is a client-side JavaScript library that can access Solr via its REST-like interface. Solr is not properly a database, but a search server; however it is the main data source for a search-based application. The result of using Ajax Solr for the user interface is literally having a JDBC (or PDO) Api available in the browser, which can make queries and change the results displayed basing on events generated by the user.
Ajax Solr is agnostic on the javascript framework to employ; by default it uses jQuery, which is quite popular, but there is no dependency towards it whatsoever.
There is already a great tutorial on Ajax Solr which explains how to build their demo application from scratch, so I do not want to repeat it here. However I will include some code to give a practical feel to this article and show how easy is to get a Solr application up and a running. The demo application is about a database of news pieces, which can be browsed and tear apart in any way you may think of. The tutorial let me build, in some hours, record visualization, their pagination, some filters to use in combination (faceted search) and chosen via a tag cloud, plus a text-based search field.
Servlets and JSP vs. Widgets and jQuery
Abandoning Lucene for Solr is one of the most ROI-savy choices I've done, and the JavaScript interface is also more responsive and dynamic than a Servlet/JSP one (assuming there is no Ajax involved in the latter). I can think of many advantages of JavaScript over generating the user interface in Java. For example, there is a very fast feedback on the code you write, and debug is no more a nightmare thanks to tools like Firebug; and no compilation step is required.
Eclipse keeps updated a build full of .class files, but you have to actually restart the application if you change some code; since the referenced Tomcat web-app folder is the original one instead (at least for HTML and JS files) while coding a JavaScript interface you can edit a file, save it and see immediately what changed by reloading the page in the browser.
Firebug is also amazing since it can introspect variables with console.log() and console.dir(); in case a variable is a DOM node like a generated link or list item, it shows an HTML representation of it. And it even shows the DOM tree both as an object and as HTML: not the original HTML like HttpUnit does, but the current one, after JavaScript widgets execution.
Moreover, JavaScript is a dynamic language, and it is very concise (it seems you can actually write JavaScript code without an IDE): today Java complained that I can't convert a Collection<Object> to a Collection<String>, while in JavaScript, if doc is a result document that comes from Ajax Solr, I usually write doc.artist and doc.title to reference its properties (where artist and title are field of the schema.) No code generation.
Add to that the ease of use of JavaScript when accessed via a framework like jQuery, which augments the DOM elements with intuitive utility methods, such as element.html() and element.css() to change content and style of a tag.
Java is great in populating Solr with documents in my back end, via an asynchronous process, but I had to render unto Caesar the things which are Caesar’s, and adopt a JavaScript-based interface for faster development and feedback. Web interfaces need feedback and experimentation all the time, and with JavaScript the only infrastructure I have to deal with is in my browser. On the back end instead, the infrastructure is stable (some sets of OSGi bundles and daemons), while if something goes wrong it's hard to tell from the client side: thus I need a compile-time checked platform that fills the log with warnings any time something is not quite right.
Testing
Even when coding an Ajax application, I won't renounce to tests: in this project I had acceptance-level tests and not unit ones because my logic is pretty coupled to presentation. If I had to do some business task like call some other web service, I would have taken also JsUnit into consideration. Tests are important because there is no compiler to discover our mistakes in JavaScript applications.
Thus, I use a Selenium test suite to crawl my interface with a real browser and ensure it works well. I did not do Test-Driven Development here since a user interface cannot be designed only via code, but it needs real visual feedback; however Selenium methods can be very loosely coupled to the page structure since you're actually driving a browser and not a DOMDocument:
selenium.open("/chansonnier/index.html");
wrapped.verifyTrue(selenium.isTextPresent("Beautiful Day"));
selenium.click("link=happiness");
wrapped.verifyTrue(selenium.isTextPresent("(x) emotion:happiness"));
wrapped.verifyTrue(selenium.isTextPresent("The heart is a bloom"));
Thus with an incremental approach, you can add a little behavior at the time and check the links are accomplishing their job.
Code samples
It's time to show some real code. This is my full bootstrap code for Ajax Solr:
$(function () {
Manager = new AjaxSolr.Manager({
solrUrl: 'http://localhost:8983/solr/'
});
Manager.addWidget(new AjaxSolr.ResultWidget({
id: 'result',
target: '#result'
}));
Manager.addWidget(new AjaxSolr.PagerWidget({
id: 'pager',
target: '#pager',
prevLabel: '<',
nextLabel: '>',
innerWindow: 1,
renderHeader: function (perPage, offset, total) {
$('#pager-header').html($('<span/>').text('displaying ' + Math.min(total, offset + 1) + ' to ' + Math.min(total, offset + perPage) + ' of ' + total));
}
}));
var fields = [ 'emotion' ];
for (var i = 0, l = fields.length; i < l; i++) {
Manager.addWidget(new AjaxSolr.TagcloudWidget({
id: fields[i],
target: '#' + fields[i],
field: fields[i]
}));
}
Manager.addWidget(new AjaxSolr.CurrentSearchWidget({
id: 'currentsearch',
target: '#selection',
}));
Manager.addWidget(new AjaxSolr.TextWidget({
id: 'text',
target: '#search',
field: 'lyrics'
}));
Manager.init();
Manager.store.addByValue('q', '*:*');
Manager.store.addByValue('rows', 1);
var params = {
facet: true,
'facet.field': [ 'emotion' ],
'facet.limit': 20,
'facet.mincount': 1,
'f.topics.facet.limit': 50,
'json.nl': 'map'
};
for (var name in params) {
Manager.store.addByValue(name, params[name]);
}
Manager.doRequest();
});
There is a Manager object, where widgets are added one at the time. Of course a much more basic setup is possible:
$(function () {
Manager = new AjaxSolr.Manager({
solrUrl: 'http://example.solrstuff.org/solrjs/'
});
Manager.addWidget(new AjaxSolr.ResultWidget({
id: 'result',
target: '#docs'
}));
Manager.init();
Manager.store.addByValue('q', '*:*');
Manager.doRequest();
});
In both cases, the setup code is wrapped in jQuery's onload handler, which you may substitute with your library's one. The first code snippet sets up a nearly complete application, with tag clouds, faceted search, search boxes, pagination and so on, while the second is a simple list of results.
Of course we have to define widgets to be able to add them to the Manager. Usually we only have to inherit from an abstract class and override a method to provide application specific code. For example, here is a ResultWidget:
AjaxSolr.ResultWidget = AjaxSolr.AbstractWidget.extend({
afterRequest: function () {
$(this.target).empty();
for (var i = 0, l = this.manager.response.response.docs.length; i < l; i++) {
var doc = this.manager.response.response.docs[i];
$(this.target).append(AjaxSolr.theme('result', doc));
var items = this.facetLinks('emotion', [doc.emotion]);
AjaxSolr.theme('list_items', '#links_' + doc.uuid, items);
}
$('.images a').lightBox();
},
// ...utility methods
});
The afterRequest() handler is called every time the search result is regenerated due to a new condition, and takes out an element from the DOM, and populates it with a representation of the various documents (this bit is jQuery-specific). In this sample code I use AjaxSolr.theme to keep HTML generation and this logic separated, but you can simply use jQuery in this method to build a list or a series of divs.
Conclusion
Summing up, there is no one true language for every type of development. Java is powerful in asynchronous and intensive tasks, while JavaScript is a really good platform for web-based interfaces. If you work on an Ajax application, you're already generating part of it on the client at every XMLHttpRequest.send() call: why not generating more of it?
Opinions expressed by DZone contributors are their own.
Comments