Monday, November 29, 2010

Sling gotcha

A content-driven application normally implements the basic CRUD operations. Using Sling this should be done relatively easy.
As part of the Read operation you normally have to implement some listing of available resources. Being familiar with JCR API  it would be natural to do it with this JavaScript code in your ESP page

var iter = currentNode.getNodes();
while (iter.hasNext()) {
    var childNode = iter.nextNode();
    ...


Yes, but no! The result is a big fat exception

org.mozilla.javascript.EcmaError: TypeError: hasNext is not a function, it is org.mozilla.javascript.Undefined. (/apps/catalog/html.esp#11)


Hm! currentNode is a JCR Node so getNodes() should return a javax.jcr.NodeIterator which is a java.util.Iterator. So the code looks correct but somehow iter here is not a Java object at all.

I struggled with this several hours. I was very puzzled to see similar code in espblog sample working just fine. The only difference is that espblog uses QueryResult.getNodes() which still returns NodeIterator.

Finally I found the answer in this thread. It turns out Sling wraps Node.getNodes() to return a JavaScript object which has one property for each child node. Probably the idea was to easily iterate over that object with a for-each loop

for each (var childNode in  currentNode.getNodes()) {
    ...


Another solution is to use the property name instead of the getter method

var iter = currentNode.nodes;
while (iter.hasNext()) {
    var childNode = iter.nextNode();
    ...


This seems to circumvent the wrapping done by Sling.

This example illustrates a cute feature of Rhino which allows easy access of JavaBeans properties.

Yes, server side JavaScript can be fun.

Monday, November 1, 2010

Dynamic Class Loading in OSGi

Sometimes you need to load an arbitrary class which you don't know in advance. In a normal Java application you would do that with Class.forName(String className). But in OSGi this will work only if your bundle imports explicitly the package of the class that you wish to load. In OSGi each bundle has its own class loader. OSGi services will not help if the class you wish to load is not exposed as a service, which is more likely the case.
It would be great if at run-time you could find the bundle exporting the package of the desired class and ask that bundle to load the class using its own class loader.
It turns out this is possible via PackageAdmin service.

org.osgi.service.packageadmin.PackageAdmin packageAdmin;
...
Class clazz = packageAdmin.getExportedPackage(packageName)
  .getExportingBundle().loadClass(className);

Here packageName is the package name and className is the full class name.
This way you can load any class from any package exported by any active bundle and still your bundle is independent from the bundle providing the class. This could be very useful when implementing some generic functionality like object persistence.

Wednesday, September 1, 2010

Ant & Xerces

Have you seen errors like this when parsing XML from Ant tasks?

org.xml.sax.SAXParseException: Invalid encoding name "Cp1252".
  at org.apache.xerces.parsers.DOMParser.parse(Unknown Source)
  at org.apache.xerces.jaxp.DocumentBuilderImpl.parse(Unknown Source)
  at javax.xml.parsers.DocumentBuilder.parse(DocumentBuilder.java:208)
  ...

The problem is in the encoding specified in the xml header
<?xml version="1.0" encoding="Cp1252"?>
This often appears in xml files generated by Java programs.

Bug 4665105 is filed for this issue but it is closed as "Not a defect".

It turns out that the same XML file is parsed fine by the JRE libraries, but ant uses its own xml libraries - xml-apis.jar and xercesImpl.jar found under ant/lib. If I delete these two files, xml parsing in ant works fine.
Luckily this issue seems solved in latest Ant 1.8.1. From the release notes:
* Ant no longer ships with Apache Xerces-J or the XML APIs but relies
on the Java runtime to provide a parser and matching API versions.

A workaround.
When parsing the xml file instead of
DocumentBuilder.parse(file)
use
DocumentBuilder.parse(new InputSource(new FileReader(file)))
This tells the parser to ignore the encoding specified in the xml header.

Friday, August 27, 2010

Backup on Linux

One of the few things for I still have to switch to Windows is backup.
As I wrote before, for that job I have put up a small bat file that relies on robocopy to do the job.
Finally I came up with a similar shell script for Linux that does the same job using rsync.
Now the backup script on Linux reads the same list file as the backup script on Windows (my NTFS drives are mounted in Linux).
It looks like this

#!/bin/sh


TARGET=/media/SAMSUNG/BACKUP
SYNC='rsync -rptgovF --delete --delete-excluded'


# Backup Windows drives
LIST=/home/peter/Documents/Documents/Backup/backup.lst
cat $LIST | fromdos | sed s/\\\\/\\//g  | while read -r line 
do
echo BACKUP $line
case "$line" in
C:*) $SYNC "/media/Windows 7${line#C:}/" "$TARGET${line#C:}";;
D:*) $SYNC "/media/Data${line#D:}/" "$TARGET${line#D:}";;
esac
done


# Backup Linux drive
echo BACKUP /home/peter
$SYNC /home/peter/ "$TARGET/home/peter"


The F option tells rsync to look for file .rsync-filter in each directory. If it is present it specifies files to exclude from that directory.
In a typical UNIX way several commands are chained via pipes.
fromdos is used to convert the text file from Windows to UNIX format, i.e. strip CR chars as they would cause problems later on.
sed is used to replace all back slashes to forward slashes.
${line#C:} and {line#D:} macros produce the path without the drive.

UNIX shell scripting proved once again to be very powerful.

Monday, August 23, 2010

Sling

Have been checking Sling recently. Basically yet another Java web framework, but this one has a rather interesting approach - it is content oriented. It is based on OSGi, JCR, REST, server side scripting, etc. The recently popular principle convention over configuration is heavily used.
Found this brief intro to Sling.
Also this cheat sheet gives a condensed overview over the Sling way.

Although both projects Sling and Jackrabbit are hosted at Apache, they are led by a company called Day.
It is interesting that Roy T Fielding is a chief scientists at Day.

Sunday, August 15, 2010

Cyrillic in Wine

As windows is still much more popular than Linux there are some Windows applications that still do not have a Linux version. In these cases WineHQ comes to rescue. It allows installing and running Windows applications directly on Linux.
One such applications that I use is AceMoney Lite. But I encountered one problem - the application does not show properly text in Cyrillic like text in Bulgarian or Russian.
The solution that works for me is to set the LANG environment variable to Bulgarian.
After running the installation of a Windows application using Wine, it appears in Applications > Wine > Programs menu. So it can be launched just like an ordinary Linux application. So I opened the properties of AcemoneyLite launcher. The command there looked like this
env WINEPREFIX="/home/peter/.wine" wine "C:\Program Files\AceMoney\AceMoney.exe" 
I inserted LANG=bg_BG.UTF-8 before wine and now the cyrillic works just fine in AceMoney Lite.

I found the same trick works also for Picasa. The Linux version of Picasa actually bundles Wine and runs on it. So there prepend env LANG=bg_BG.UTF-8 to the command so it looks something like this
env LANG=bg_BG.UTF-8 /opt/google/picasa/3.0/bin/picasa

...

UPDATE 2 Feb 2012   
Recently this stopped working in Ubuntu 11.10.
After several attempts I found that if I use LC_ALL=bg_BG.UTF-8 instead of LANG=bg_BG.UTF-8, it works again.

Saturday, August 14, 2010

Ubuntu

Since Windows 7 beta started complaining that it expires I have been trying again Ubuntu (10.04 Lucid Lynx).
Although it has improved significantly still the user experience is not as smooth as in Windows.  For example such basic things as DVD playback and Flash do not work out of the box.

I will try to share my findings about Ubuntu.

For me one of the major new features in Windows Vista is the search box in the start menu. No need to navigate through menus to find some program or browse through directories to locate some document, just press the Win key and start typing the name. A similar thing exists in Linux - Deskbar-Applet. And its even more powerful and flexible. Deskbar supports many extensions (searches) which interpret the search string in different ways. For example it can do simple calculations, can search in Google, Wikipedia, dictionary and many other things.

This is the Linux way - initially hard to setup but afterwards it is very powerful.

Wednesday, March 17, 2010

7-Zip

The guys at Tom's Hardware have done some benchmarks of recent compression tools.
It turns out 7-Zip is most efficient in terms of compression speed and ratio.
It can also extract many other formats like RAR.
Considering that this tool is also free and open source you may seriously consider switching to it.

Saturday, March 6, 2010

Extracting MPEG2 streams from DVD-RAM

DVD-RAM
Using DVD-RAM discs with camcorders is quite convenient. Recording on this meadia is much more reliable. You can also edit your videos on the camera - delete, cut, join etc. The downside is incompatibility. Most DVD players do not play DVD-RAM discs. But recent PC optical drives do read them. So when I get the disc out of my camera and put it in my PC, it shows this file system


C:\temp>dir e:\DVD_RTAV
 Volume in drive E is DVD_CAMERA
 Volume Serial Number is 57DC-951A

 Directory of e:\DVD_RTAV

31.01.2010  16:42    <DIR>          .
31.01.2010  16:42    <DIR>          ..
03.03.2010  16:49            11 961 VR_MANGR.IFO
03.03.2010  16:49            11 961 VR_MANGR.BUP
03.03.2010  16:49       713 383 936 VR_MOVIE.VRO
               3 File(s)    713 408 142 bytes
               2 Dir(s)     688 580 608 bytes free

Actually it seems the video (and audio) is recorded in MPEG2, just like in ordinary DVD but it is packaged differently (probably to facilitate editing on the disc).

DVD-VR
Fortunately a nice guy Pádraig Brady wrote a simple open source tool dvd-vr that extracts MPEG2 streams from DVD-RAM. It is written for Linux but with cygwin it compiles to dvd-vr.exe. dvd-vr extracts each video clip into a separate .vob file. You can play these directly on your PC. If your media player does not recognize these files, you can rename them to .mpeg.
Having a separate file for each clip is very convenient. You can arrange them as you see fit, much like your photos.
You can also record selected clips on a regular DVD to share with your friends. You can do this with various tools like DVD Flick for example - another free and easy to use app.

Happy shooting!

Tuesday, February 23, 2010

Integrating Wicket and JCR


As requested I will describe briefly my take at integrating Wicket with JCR (Jackrabbit).
It is pretty straightforward.

Open/close JCR repository
My override of org.apache.wicket.protocol.http.WebApplication.init() looks up a javax.jcr.RepositoryFactory implementation via a java.util.ServiceLoader. Then I use this factory to open the repository. I store a reference to the repository in a field of my application class.
My override of producat.wicket.Application.onDestroy() closes the repository via org.apache.jackrabbit.api.JackrabbitRepository.shutdown().

Open/close JCR session
I try to keep a JCR session open only for the duration of a HTTP request.
To achieve this my application class overrides org.apache.wicket.protocol.http.WebApplication.newRequestCycle(Request, Response) to provide a custom request cycle implementation.

    @Override
    public RequestCycle newRequestCycle(Request request, Response response) {
        return new MyRequestCycle(this, (WebRequest) request, response);
    }

My implementation opens JCR session on demand and closes it at the end of the request cycle.
It looks similar to this class.

public class MyRequestCycle extends WebRequestCycle {

    private Session session;

    public MyRequestCycle(WebApplication application, WebRequest request,
            Response response) {
        super(application, request, response);
    }

    @Override
    protected void onEndRequest() {
        if (session != null) {
            session.logout();
            session = null;
        }
    }

    public Session getJcrSession() {
        if (session == null) {
            Repository repository = ((MyApplication) getApplication()).getRepository();
            session = repository.login();
        }
        return session;
    }

    public static MyRequestCycle get() {
        return (MyRequestCycle) RequestCycle.get();
    }
}

Then I use MyRequestCycle.get().getJcrSession() in my page classes to access the repository.
I save the session explicitly in every place where I make changes in the repository. Initially I thought of saving it automatically at the end of the session, but then it turned out to be difficult to handle decently save errors.

Data models and URLs
I try to avoid HTTP session as much as possible so I use BookmarkablePageLink's, passing the UUID of respective JCR node in PageParameters.
Since JCR nodes are not serializable, my models store only the node UUID and load it on demand via javax.jcr.Session.getNodeByIdentifier(String) - kind of LoadableDetachableModel.