Showing posts with label JavaScript. Show all posts
Showing posts with label JavaScript. Show all posts

Tuesday, November 8, 2011

Easy XSLT without installing anything


Sometimes you need to apply an XSLT to some XML, and there are (at least) a couple of ways to do it:

(a) Install a processor like Saxon and run it through the command line;
(b) Reference an XSLT from your XML file, and then open it in a browser;

Both are somewhat problematic - the first one requires installing and running something, and the second requires modifications to the XML. I propose a useful and effective alternative, which I've been using successfully for quite a while. The idea is as following: you simply open a special HTML in a browser. This HTML contains a JavaScript that loads both XML and XSLT files, applies the transform and renders the result into its own DOM. Here is the HTML I use, it is really simple and works in both IE and Mozilla. All you need to do is replace the filenames (input.xml and template.xsl):
<html>
<head>
<script>
function loadXMLDoc(fname)
{
var xmlDoc;
if (window.ActiveXObject)
{
xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
}
else if (document.implementation && document.implementation.createDocument)
{
xmlDoc = document.implementation.createDocument("","",null);
}
else
{
alert('Your browser cannot handle this script');
}
xmlDoc.async = false;
xmlDoc.load(fname);
return(xmlDoc);
}

function displayResult()
{
xml = loadXMLDoc("input.xml");
xsl = loadXMLDoc("template.xsl");
if (window.ActiveXObject)
{
ex = xml.transformNode(xsl);
document.getElementById("content").innerHTML = ex;
}
else if (document.implementation && document.implementation.createDocument)
{
xsltProcessor=new XSLTProcessor();
xsltProcessor.importStylesheet(xsl);
resultDocument = xsltProcessor.transformToFragment(xml,document);
document.getElementById("content").appendChild(resultDocument);
}
}
</script>
</head>
<body onLoad="displayResult()">
<div id="content" />
</body>
</html>

A sample use case: you have an application which logs some data into an XML file at a given location. You write an XSLT that filters errors in this log, and adjust the filenames inside the HTML above. You save this HTML on your Desktop, and every time you open or refresh it, it shows you the current errors in the log. It is easier and more user-friendly than running BAT-files saving output of transform into some temporary HTML and then opening it.

Friday, January 29, 2010

WebSphere + GWT + Comet

IBM supports Reverse AJAX (AKA Comet) in a form of WebSphere Feature Pack for Web 2.0 for WebSphere Application Server 6.0+. It is shipped by default with the latest fix packs for WAS 6.1 and in all 7.0 versions. Actually this Feature Pack has a lot of nice Web 2.0 features, but at the moment we are interested in Web Messaging Service only.

To install and enable FP you need to follow the documentation, it won't take long. As the result you will get the ability to push data to your client's web browser by publishing it to the special JMS topics. As an alternative, you may also use a more specialized API for doing this, but the Publisher object is stored in the Servlet context, therefore it can be somewhat tricky to access it from an arbitrary part of your application, which is not true with JMS.

I assume that you have successfully installed and tested your Web Messaging, and now want to upgrade your QuoteStreamer sample application to GWT. For example, you have a widget and want its methods to be called when some data arrives from the backend.

It is possible to use JSNI to achieve exactly this goal. The idea is simple: when you need to push some object to the client (for example, out of your Message Driven Bean), you first serialize it using GWT RPC mechanism, then put the resulting string to the appropriate JMS Topic. This string is wrapped to JSON envelope, sent to the client via WFP and unwrapped there via Dojo. Your JavaScript listener should be triggered at this moment. But as we use GWT for all our JavaScript programming, we should implement that listener as JSNI snippet:
private native void initCallbackAndSubscribe (String someParam) /*-{
if ($wnd.dojox) {
$wnd.dojox.cometd.subscribe("/test/" + someParam, this,
function (comet) {
module.@com.test.MyCoolWidget::onReverseAjax(Ljava/lang/String;)(comet.data)
});
}
}-*/;

// This is a normal Java code
public void
onReverseAjax(String msg) throws SerializationException {
SerializationStreamReader r = ((SerializationStreamFactory) svc).createStreamReader(msg);
Object data = r.readObject();
// Do something with that data arrived
}
As you see, the data is deserialized later in your normal Java code called by this listener. That's it. If you have initialized everything right (in your index.jsp or some similar place), this scheme should work fine. Now I will show you how to send data using GWT RPC. It should be simple via RPC.encodeResponseForSuccess method, but there is one problem - it requires SerializationPolicy object for the security reasons. This mechanism restricts serialized objects to the limited set described in *.gwt.rpc files, generated during compilation. Another problem is that there can be several such files. I haven't found a good way to get the SerializationPolicy other than by concatenating these files altogether:
public SerializationPolicy getSerializationPolicy() throws Exception {
String result = "";
for (File file : new File(PATH_TO_YOUR_GENERATED_STUFF).listFiles(new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.endsWith(".rpc");
}
})) {
result += (FileUtils.readFileToString(file));
}
return SerializationPolicyLoader
.loadFromStream(new ByteArrayInputStream(result
.getBytes("UTF-8")), null);
}

public void publishObject(String topic, Object obj, Method method) throws Exception {
String r = RPC.encodeResponseForSuccess(method, obj, getSerializationPolicy());
publishStringViaJms(topic, r.toString());
}
I forgot to mention that RPC.encodeResponseForSuccess requires a Method instance, but it is used only to extract the return value type, so any method returning the object of the obj.getClass() type will do.

In general, there are some good things about such WebSphere + GWT + Comet integration, the major one is that it uses WFP, which is a pretty powerful Comet server, compared to some custom-built solutions. For example (at least on WAS 7.0), it uses connection multiplexing, nio and all that cool stuff you expect from the modern web server. Also, it naturally integrates into your ESB, so you can use it in your Process Server applications with ease. Some limitations are obvious too, for example it won't work in GWT Hosted Mode with Tomcat. Also it seems to me that this should be implemented somehow simpler, but I was unable to find it. So if you have any other ideas, please give me a clue.

P.S.: Actually, there are some alternatives to this approach. For example you can use gwt-comet-streamhub to enable Reverse AJAX for your GWT applications. But it provides its own Comet server called StreamHub which is definitely not a part of the IBM WebSphere product line :)

P.P.S.: The described method should work with other Comet-enabled J2EE servers like Jetty with minor modifications.

P.P.P.S.: There is left one more issue with security. As for now, it is not really clear for me how to implement role-based security for Comet... I can think of using several update servlets at the same time, and it should allow me to use webapp security. But this issue definitely needs further investigation.

Friday, June 5, 2009

Client JavaScript data storage

Cross-browser data storage made easy using PersistJS. It supports a lot of different ways of storing data on client browser, falling back to plain old cookies, when necessary. The library itself is really minimalistic and standalone (opposed to Dojo Storage, for example). Also, it can be integrated with another lightweight runtime storage solution called "Taffy DB", which will enable almost SQL-like queries in JavaScript. Looks very nice to store some user-related info, session data, etc.