Sunday 27 May 2007

On using the echo2 googlemaps component

There is a echo2 component for google maps
here. And it works really good so far.

The author of this component says that the echo2 code has to be patched such that the google map api can be loaded at page load time which is needed for google maps (at least with echo2 version 2.1.0beta5).

I managed to get the component running with echo2 2.1.0rc2 without patching:

The starting point for an echo2 application is a servlet which extends nextapp.echo2.webrender.WebRenderServlet. The normal behaviour of this servlet is to register some services. One of these services is the nextapp.echo2.webcontainer.WindowHtmlService which produces the output in its service method. The trick is now to replace this class with your own implementation.

I made it this way:

The constructor of my HelloWorldServlet which extends nextapp.echo2.webrender.WebRenderServlet and which is the starting point of my echo2 application is the following:

public HelloWorldServlet()
{
super();

final String METHOD = "HelloWorldServlet: ";

ServiceRegistry serviceRegistry = WebRenderServlet.getServiceRegistry();
serviceRegistry.remove(WindowHtmlService.INSTANCE);
serviceRegistry.remove(NewInstanceService.INSTANCE);
serviceRegistry.add(MyNewInstanceService.INSTANCE);
serviceRegistry.add(MyWindowHtmlService.INSTANCE);

log.debug(METHOD+"successful");
}

This removes the old service implementations and replaces it with my own implementations:

package de.banapple.echo2test;

import java.io.IOException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import nextapp.echo2.webcontainer.ContainerInstance;
import nextapp.echo2.webrender.Connection;
import nextapp.echo2.webrender.Service;

public class MyNewInstanceService
implements Service
{
private static Log log = LogFactory.getLog(MyNewInstanceService.class);

public String getId()
{
return "Echo.NewInstance";
}

public int getVersion()
{
return -1;
}

public void service(Connection conn)
throws IOException
{
final String METHOD = "service: ";
log.debug(METHOD+"conn="+conn);

ContainerInstance.newInstance(conn);
MyWindowHtmlService.INSTANCE.service(conn);
}

public static final MyNewInstanceService INSTANCE =
new MyNewInstanceService();

}



package de.banapple.echo2test;

import java.io.IOException;

import nextapp.echo2.webcontainer.ContainerInstance;
import nextapp.echo2.webcontainer.WindowHtmlService;
import nextapp.echo2.webrender.BaseHtmlDocument;
import nextapp.echo2.webrender.Connection;
import nextapp.echo2.webrender.ContentType;
import nextapp.echo2.webrender.output.CssStyle;
import nextapp.echo2.webrender.service.CoreServices;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Element;

public class MyWindowHtmlService
extends WindowHtmlService
{
private static Log log = LogFactory.getLog(MyWindowHtmlService.class);

public void service(Connection conn)
throws IOException
{
final String METHOD = "service: ";
log.debug(METHOD+"conn="+conn);

ContainerInstance ci = (ContainerInstance)conn.getUserInstance();
conn.setContentType(ContentType.TEXT_HTML);
boolean debug = !"false".equals(conn.getServlet().getInitParameter("echo2.debug"));
BaseHtmlDocument baseDoc = new BaseHtmlDocument("c_root");
baseDoc.setGenarator("NextApp Echo v2.1.0.rc2");

/* add google maps api */
baseDoc.addJavaScriptInclude(
"js/googleload.js");

baseDoc.addJavaScriptInclude(ci.getServiceUri(CoreServices.CLIENT_ENGINE));
baseDoc.getBodyElement().setAttribute("onload", "EchoClientEngine.init('" + ci.getServletUri() + "', " + debug + ");");
Element bodyElement = baseDoc.getBodyElement();
CssStyle cssStyle = new CssStyle();
cssStyle.setAttribute("position", "absolute");
cssStyle.setAttribute("font-family", "verdana, arial, helvetica, sans-serif");
cssStyle.setAttribute("font-size", "10pt");
cssStyle.setAttribute("height", "100%");
cssStyle.setAttribute("width", "100%");
cssStyle.setAttribute("padding", "0px");
cssStyle.setAttribute("margin", "0px");
cssStyle.setAttribute("overflow", "hidden");
bodyElement.setAttribute("style", cssStyle.renderInline());
baseDoc.render(conn.getWriter());
}

public static final MyWindowHtmlService INSTANCE =
new MyWindowHtmlService();
}


The implementation of the service method of MyWindowHtmlService is copied from the original implementation and extended with the
baseDoc.addJavaScriptInclude("js/googleload.js"); which loads the google maps api with my key.

Ok, that is not really far away from patching the class, but still... It might work with forthcoming versions of echo2.

1 comment:

jl2tho said...

Hello Hacim, nice post on mashup and Echo2. You can found on my my blog at http://jl2tho.blogspot.com/2007/06/tutorial-mashup-google-map-avec-echo2.html (sorry it's in french) a solution without any modification of Echo2. The solution use the component HttpPaneEx from EchoPointNG (I guess this is standard library for most Echo2 users). This component creates an HTML Iframe in which you can display any HTML pages : even one using GoogleMaps.
Next, you need to interact with it. The trick is to use method enqueueCommand and JavaScriptInclude and JavaScriptEval object from Echo2 to trigger javascript from Echo2.
This mechanism, use only 100% Public API and no overloalong of any basic Echo2's mechanism.
Notice, that trying to use directly this mechanism (jJvaScriptInclude) on GoogleMaps object to add it directly in the Echo2 application (without HttpPaneEx) provoke a strange behavior : the browser keep trying loading the google map on a blank screen.