-
Get a monthly update on best practices for delivering successful software.
Note: the screenshots in the post are all generated by the JRex solution, not print screen.
So I had a reader call me out -- correctly -- that a HOWTO article should really contain some code, not just pointers and handwaving on how things might be done. So I've decided to make amends by following through on my article from two weeks ago on how to do page preview. Here are my experiences, some code, and -- once I've cleaned up the code -- a zip file so you can try your own hand at embedded HTML rendering.
So I tried each of the HTML renderers from my article in turn. The results were less than promising.
The rest of this post is about my experiences with JRex. A few preliminaries: first, I developed and deployed this solution on Windows (XP and Advanced Server). It should be possible to do so on Linux (JRex runs there same there as on Windows). Next, the code is a bit of a hack. This was definitely a keep-hacking-until-it-works kind of effort. My hope is that someone else can come along and take this to the next level.
Hello World with JRex
The documentation for JRex leaves something to be desired, so the first challenge is installing the thing and getting it to run. I went with the binary download of JRex 1.0b1_dom3 from September 8th, 2005. You need the binary WithOutLog download and the jrex_gre dowload. Unzip the zip file into C:/jrex, then unjar the second download to a scratch directory. In the unjared material, find the file org\mozilla\jrex\jrex_gre.zip. Unzip this to C:/jrex/jrex_gre. Finally, move the file jrex.dll from C:/jrex to C:/jrex/jrex_gre.
Now we are ready to write a little "Hello World!" program, just to test if everything is working. The following is a minimal program that launches a browser window:
package test;
import org.mozilla.jrex.JRexFactory;
import org.mozilla.jrex.ui.JRexCanvas;
import org.mozilla.jrex.window.JRexWindowManager;
import javax.swing.*;
public class JRexTest {
public static void main(String[] args) {
try {
JRexFactory.getInstance().startEngine();
} catch (Exception e) {
System.err.println("Unable to start up JRex Engine.");
e.printStackTrace();
System.exit(1);
}
JRexWindowManager winManager=(JRexWindowManager)
JRexFactory.getInstance().getImplInstance(JRexFactory.WINDOW_MANAGER);
winManager.create(JRexWindowManager.SINGLE_WINDOW_MODE);
JPanel inner = new JPanel();
JFrame frame = new JFrame();
frame.getContentPane().add(inner);
winManager.init(inner);
frame.setSize(640, 480);
frame.setVisible(true);
}
}
Compile and then run with a JRE (otherwise under certain circumstances JRex may not link to the awt DLL):
C:\Java\jdk1.5.0_08\jre\bin\java.exe -Djrex.gre.path=C:/jrex/jrex_gre text.JRexTest
Once we've come this far we're ready to move to the web side of things.
Preview Webapp
I decided to build a webapp that produces PNG image screenshots. My first thought was to simply build a servlet and use the headless trick to grab the output of the JRex components. There are a couple of issues that complicate matters, however:
To solve the problem of the application server, I used the dead simple embedded web server NanoHTTPD. If you need something dead simple, this is a handy way to go.
On to the actual image creation. Our web page will be in a JFrame on the display. We need a way to capture that JFrame to an image. We do this in SwingImageCreator using screen capture:
public static BufferedImage snapShot(Rectangle rect) throws AWTException {
return new Robot().createScreenCapture(rect);
}
public static BufferedImage snapShot(JFrame frame) throws AWTException {
Point pt = frame.getLocation();
return snapShot(new Rectangle(pt.x, pt.y, frame.getWidth()+pt.x, frame.getHeight() + pt.y));
}
The screen capture requires that our frame be on top. We do this in the PageRendererImpl class by calling frame.setAlwaysOnTop(true):
public synchronized BufferedImage renderPage(String url) {
try {
createBrowser();
navigation.loadURI(url, WebNavigationConstants.LOAD_FLAGS_NONE, null, null, null);
frame.setAlwaysOnTop(true);
try {
Thread.sleep(THIRTY_SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
BufferedImage img = SwingImageCreator.snapShot(frame);
frame.setAlwaysOnTop(false);
return img;
} catch (Exception e) {
e.printStackTrace();
return new BufferedImage(DEFAULT_IMAGE_DIM, DEFAULT_IMAGE_DIM, BufferedImage.TYPE_INT_ARGB);
}
}
The method is synchronized to prevent more than one thread for accessing the screen (of which we have only one).
The first thing to notice here is that we use a hack, e.g. sleeping for 30 seconds instead of detecting via an event listener whether the page has rendered. The next is that we open a new browser window each time. Ideally we would know if there is a popup or some other issue with the JRex browser, but instead we just nuke it and create another browser. One other way we prevent unwanted popups is by creating the window manager in JRexWindowManager.SINGLE_WINDOW_MODE, in which mode the browser will not open other tabs or windows. Still, if you enter a URL that doesn't load, you get something like this:
The createBrowser method also creates the Navigator object that is responsible for loading the page:
private void createBrowser() {
if (frame != null) {
// destroy and create anew
frame.dispose();
}
frame = new JFrame();
JPanel inner = new JPanel();
frame.getContentPane().add(inner);
mgr.init(inner);
canvas = (JRexCanvas) mgr.getBrowserForParent(inner);
Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
frame.setSize((d.width> BROWSER_WIDTH ? BROWSER_WIDTH :d.width), d.height - BROWSER_HEIGHT_DELTA);
frame.setLocation(0,0);
navigation = canvas.getNavigator();
}
This solution certainly has plenty of warts, but it sort of works. It will take more time than I have to dig into the code of JRex and discover what can be done to make it a renderer instead of an app or to at least detect error conditions such as modal dialogs.
I'll clean up the code soon and post a link here to the zip file.
Related posts:
Topics: Ajax Development, Firefox
It’s certainly a hacky way to make it all work – maybe using a HTML Render isn’t the best solution afterall.
The other day I found a company with similar solution but they tackle it by dealing with display memory directly (http://www.guangmingsoft.net/).
It really makes you wonder how snap.com generates all those preview images…
Comment by gogobu, Sunday, January 28, 2007 @ 12:32 am
Hallo, please post link to the zip Ajax preview page…thx
Comment by Oggy, Saturday, April 28, 2007 @ 1:57 pm
I think there is an error at the command:
“C:\Java\jdk1.5.0_08\jre\bin\java.exe -Djrex.gre.path=C:/jrex/jrex_gre text.JRexTest”
your package is named “test” not “text”.
I have a suggestion for use the code line: “System.setProperty(”jrex.gre.path”,”C:/jrex/jrex_gre”);”
This semplify the command line.
I hope this article is very useful.
bye
Comment by roli, Wednesday, October 24, 2007 @ 12:28 pm
Thank you Dietrich for the tutorial, it is well explained.
I have done all steps but I have the Exception:UnsatisfiedLinkError.
Have you any idea what can I do ?
Comment by Radhouane, Saturday, November 17, 2007 @ 5:04 pm
Hi, this is a year old but perhaps you can still help. I’m not familiar with packages like this and how to use them. I’m still learning java really. I attempted to compile JRex with Eclipse and that was a disaster. This seems simpler since you have the already compiled binaries, yet i cannot get your example code to compile. Do I need to change my java classpath? Right now i have all default settings and i use textpad to compile on Windows Vista. Should i not be able to create “JRexTest.java” in C:/jrex/jrex_gre and compile it? it has errors that say package org.mozilla.jrex doesn’t exist
I’m just trying to get a java browser i can modify that supports javascript, Thanks
Comment by Harry, Tuesday, January 22, 2008 @ 5:33 pm
Hi, this is a year old but perhaps you can still help. I’m not familiar with packages like this and how to use them. I’m still learning java really. I attempted to compile JRex with Eclipse and that was a disaster. This seems simpler since you have the already compiled binaries, yet i cannot get your example code to compile. Do I need to change my java classpath? Right now i have all default settings and i use textpad to compile on Windows Vista. Should i not be able to create “JRexTest.java” in C:/jrex/jrex_gre and compile it? it has errors that say package org.mozilla.jrex doesn’t exist
I’m just trying to get a java browser i can modify that supports javascript, Thanks
Comment by Harry, Tuesday, January 22, 2008 @ 5:35 pm
sorry for the double post, I figured out how to add jrex.jar to the classpath and it compiles, but i have an error when i run it(in textpad)
***************** JRexL inited *****************
***************** JREX-Logging disabled *****************
Exception in thread “main” org.mozilla.jrex.exception.JRexException
at org.mozilla.jrex.xpcom.JRexXPCOMImpl.startXPCOM(JRexXPCOMImpl.java:143)
at org.mozilla.jrex.JRexFactory.startEngine(JRexFactory.java:222)
at test.JRexExample.main(JRexExample.java:50)
Caused by: java.lang.Exception: is set to null!!!
at org.mozilla.jrex.xpcom.JRexXPCOMImpl$1.run(JRexXPCOMImpl.java:149)
Comment by Harry, Tuesday, January 22, 2008 @ 7:03 pm
um… me again
I added this >>> System.setProperty(”jrex.gre.path”,”C:/jrex/jrex_gre/”);
still doesn’t work… sorry for posting repeatedly
Exception in thread “main” org.mozilla.jrex.exception.JRexException
at org.mozilla.jrex.xpcom.JRexXPCOMImpl.startXPCOM(JRexXPCOMImpl.java:143)
at org.mozilla.jrex.JRexFactory.startEngine(JRexFactory.java:222)
at test.JRexExample.main(JRexExample.java:52)
Caused by: java.lang.UnsatisfiedLinkError: org.mozilla.jrex.xpcom.JRexXPCOMImpl.
InitXPCOM(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
at org.mozilla.jrex.xpcom.JRexXPCOMImpl.InitXPCOM(Native Method)
at org.mozilla.jrex.xpcom.JRexXPCOMImpl.access$200(JRexXPCOMImpl.java:45)
at org.mozilla.jrex.xpcom.JRexXPCOMImpl$1.run(JRexXPCOMImpl.java:173)
Comment by Harry, Tuesday, January 22, 2008 @ 7:24 pm
me again!
i found out the cause of my previous error was not following directions
i forgot to change the path from JDK to JRE… jawt.dll couldn’t be found and it threw me an error
Comment by Harry, Wednesday, January 23, 2008 @ 4:02 pm
Can u explain me wht changes did u make as im also getting the same error
Comment by anu, Tuesday, June 17, 2008 @ 6:56 pm
Unable to start up JRex Engine.
org.mozilla.jrex.exception.JRexException
at org.mozilla.jrex.xpcom.JRexXPCOMImpl.startXPCOM(JRexXPCOMImpl.java:143)
at org.mozilla.jrex.JRexFactory.startEngine(JRexFactory.java:222)
at test.JRexTest.main(JRexTest.java:12)
Caused by: java.lang.Exception: is set to null!!!
at org.mozilla.jrex.xpcom.JRexXPCOMImpl$1.run(JRexXPCOMImpl.java:149)
Comment by kkk, Thursday, September 3, 2009 @ 9:01 am
I also needed a little help to get this running. Followed install instruction. To compile I used
javac -classpath “.;c:\jrex\JRex.jar” *.java
to execute:
C:\Progra~1\Java\jdk1.6.0_17\jre\bin\java -cp “.;c:\jrex\JRex.jar” -Djrex.dom.enable=true -Djrex.gre.path=c:\jrex\jrex_gr JRexTest
This was on a windows xp system probably doesn’t apple to other OS
Comment by coachphil5, Saturday, December 19, 2009 @ 2:00 pm
copy jgre.dll in eclipse workspace to avoid problems
Comment by Luis, Friday, January 8, 2010 @ 3:36 am