23 May 2009

Cross-domain data retrieval in client-side mashups

Browsers do not allow dynamic cross-domain data retrieval to prevent cross-site scripting attacks. However, client-based mashups need to access data sources that reside on different servers. There are several solutions to this problem:

  • Proxies: requires running proxy on intermediate server, leads to slower responses and additional traffic

  • Window.name transport: requires support by the accessed services, because window.name property has to be set

  • JSON and dynamic <script> tags: requires that the accessed services expose JSON, which is e.g. not the case for RSS feeds (which use XML)

  • Signed JavaScript: only available for Firefox

  • W3C Cross-Origin Resource Sharing: needs support on client and accessed service side, only available for
    Firefox 3.5 and Internet Explorer 8

  • Flash drop-in: requires Flash on the client and a crossdomain.xml file on the server with the accessed service that support the crossdomain access.

Because all the solutions except the proxy restrict either the browsers or the web services that can be used, I decided to go with the proxy solution. I downloaded the PHP proxy script from Abdul Qabiz Blog and modified it as suggested by the comments on his blog post. I also added a small section to make sure the default content type is XML:

if ($mimeType != "") {
// set header from mime type
header("Content-Type: ".$mimeType);
} else {
// assume web service returns xml
header("Content-Type: text/xml");
}
The proxy script works for me in a development environment now, but I do not recommend using it in a production environment, because important features such as restrictions of the client domain and forwarding the headers from the original service response are missing.

Google map in dojo FloatingPane

I created a small example that shows how to run Google Maps in a dojo FloatingPane. It can be found here. The code has been tested with Firefox 3.0 and Internet Explorer 8.0 .

The map uses the complete content canvas that is available in the FloatingPane:
<div id="map_container">
<div id="map" style="width:100%;height:100%;"></div>
</div>
In the JavaScript part, a FloatingPane is created for the map_container element, the map widget is created and the resize events from the FloatingPane are forwarded to the map widget using dojo.connect:
var floatingPane = createFloatingPane("map_container");
// ... (some checks etc)
var mapElement = dojo.byId("map");
var map = new GMap2(mapElement);
// ... (map configuration)
function resize() {
map.checkResize();
}
dojo.connect(floatingPane,"resize", resize);
The example also has function that creates a dojox.layout.FloatingPane from a set of parameters. It contains a minor fix for some problems I had with the dragging of automatically created FloatingPane widgets in Firefox 3.0:
function createFloatingPane(
divId, title, x, y, width, height) {

var pane = new dojox.layout.FloatingPane({
'title': title,
'id': divId + "_floater",
'closeable': true,
'resizable': true,
'dockable': false
}, divId);

// quick fix for positioning, does not seem
// necessary in dojo source code test
// (FloatingPane test), but was necessary with
// dojo binaries and Firefox 3.0.10
pane.domNode.style.left = x + "px";
pane.domNode.style.top = y + "px";
pane.resize({ 'w': width, 'h': height });

pane.startup();

return pane;
}

19 May 2009

Running Persevere on Amazon EC2

Today I looked into running Persevere on Amazon servers, namely using their Elastic Compute Cloud (EC2). Persevere is a schema-free DB with a JSON/REST interface. It also provides a web front end for easy access.

I found their Amazon Web Services (AWS) Management Console pretty usable - it makes it really easy to configure the running instances, the block storage and the elastic IPs. I ran an instance with the "Basic Fedora Core 8" Amazon Machine Image (AMI). For this purpose, I also created a security group 'test'. Using this Putty for EC2 guide, I was quickly able to connect to the server using SSH.

I created a 1GB elastic block storage (EBS) volume and connected it to the running EC2 instance. The EBS volumes are stored persistently, even if the EC2 instance is terminated (which means all data in the instance is lost, and it can happen due to hardware failures). Furthermore, snapshots can be taken easily using the AWS management console. To use it from the EC2 instance, the volume has to be formatted and mounted (from the SSH console):

mkfs -t ext3 /dev/sdf
mkdir /mnt/volume_1
mount /dev/sdf /mnt/volume_1

I downloaded Java & Persevere to the mounted volume using wget and unpacked them into the /mnt/volume_1/opt/java and /mnt/volume_1/opt/persevere folders (short guide for Java). For real-world usage, it would be better to create a customized AMI that contains & starts them, but I wanted to try things out quickly.

Persevere started up fine (using java -jar startup.jar), but port 8080 was initially blocked by the Amazon firewall. Using the AWS management console, I added an allowed connection for TCP port 8080 (both from and to) and source 0.0.0.0/0. That way, I could access the Persevere web interface (using the Public DNS of the runnning EC2 instance which is available in the management console and appending port 8080).

The next step was securing the access to Persevere (note: before starting with this, I created a Persevere user from the web UI). Persevere uses Jetty, so I tweaked a couple of settings in the Jetty configuration. First, I configured Jetty to use HTTP over SSL. Then I set up the user authentification. For this, I had to configured a user realm (HashUserRealm), which was pretty straightforward. I used plain passwords for testing purposes, but for more serious undertaking encrypted passwords or hash sums and a database storage are more appropriate. After creating the user realm, I modified the Persevere WEB-INF/web.xml to restrict the access to the web UI. I also switched off port 8080 and modified the firewall setting in the AWS management console accordingly. After those changes (and restarting), the Persevere web UI was running on port 8443 over SSH, and required me to log in. Interestingly, I had to use the same user name and password in the user realm and the internal Persevere user, and I got signed in the Persevere web UI automatically. Logging out did not work though.

I also tried out the elastic IP service, which assigns an IP to an EC2 instance. I lost the SSH connection to the EC2 instance after this, I believe it was because the IP address changed. After rebooting the EC2 instance and reconnecting to the new IP address, I had no problems (although I needed to mount the EBS volume and start Persevere again).

Overall, I was pretty impressed how easy it is to set up services on EC2. Also, an initial securing of Persevere turned out to be easy. While I liked the technical side of things pretty much, EC2 is too expensive for my use case (having a private server on the web where I can deploy some customized services and programs). The instance hour of an EC2 instance is 0.10 USD - if you run your server 24/7, this is 876 USD / year, not counting data traffic and storage. If you want to do your own calculation, take a look at the AWS calculator.

12 May 2009

Using Dojo DataGrid in Google Gadget

I spent some time last week working with Google Gadgets and Dojo. I had some trouble getting the DataGridrid running in iGoogle at first, so here is a short snippet that shows an example dojo DataGrid running in a Google Gadget.

One mistake I made was calling the initialization methods without using dojo.addOnLoad by registering them just as callbacks for the google onload functionality. That way, the dojo classes were not loaded as expected, which resulted in a lot of weird errors. The solution is calling dojo.addOnLoad from within the callback:

gadgets.util.registerOnLoadHandler(function() {
dojo.addOnLoad(someExampleInitializationFunction);
});

The complete code for the gadget (using dojo 1.3.1) is below:

<?xml version="1.0" encoding="UTF-8" ?>
<Module>
<ModulePrefs title="Dojo Grid Example"
height="400" scrolling="true" />

<Content type="html">
<![CDATA[

<style type="text/css">
@import url("http://ajax.googleapis.com/
ajax/libs/dojo/1.3.1/dojo/resources/dojo.css");
@import url("http://ajax.googleapis.com/
ajax/libs/dojo/1.3.1/dijit/themes/tundra/
tundra.css");
@import url("http://ajax.googleapis.com/
ajax/libs/dojo/1.3.1/dojox/grid/resources/
Grid.css");
@import url("http://ajax.googleapis.com/
ajax/libs/dojo/1.3.1/dojox/grid/resources/
tundraGrid.css");
</style>

<script type="text/javascript"
src="http://ajax.googleapis.com/ajax/
libs/dojo/1.3.1/dojo/dojo.xd.js"></script>

<script type="text/javascript">

dojo.require("dojo.data.ItemFileReadStore");
dojo.require("dojox.grid.DataGrid");

function init() {

var dataItems = {
identifier: 'id',
label: 'title',
items: [{
'id': 0,
'title': 'This blog',
'url': 'http://lgrammel.blogspot.com/'
},{
'id': 1,
'title': 'My homepage',
'url': 'http://larsgrammel.de/'
}]
};

var store =
new dojo.data.ItemFileReadStore({data: dataItems});

var structure = [{
cells: [{
field: 'title',
name: 'Title',
width: 'auto'
}, {
field: 'url',
name: 'Address',
width: 'auto'
}]
}];

var grid = new dojox.grid.DataGrid({
'store': store,
'structure': structure
}, 'gridNode');

grid.startup();
};

gadgets.util.registerOnLoadHandler(function() {
dojo.addOnLoad(init);
});

</script>

<div id="gridNode"></div>

]]>
</Content>

</Module>