Watermarking Images with Java ExtensionsContent and Intellectual Property protection is a vital issue for any web site providing paid-for content. The Login Abuse article describes how ZXTM can be used to detect when a username and password is reused from different locations; this article showcases the power of ZXTM 5.0's Java™ Extensions to apply a dynamically-generated, visible watermark to every image served up by a load-balanced website. This article covers:
For more information on Java Extensions, you may like to read the What are Java Extensions article. PrerequisitesBefore you begin, make sure that you have:
If you are using a different Java IDE, or simply building Java classes using the Configure the ZXTM to load-balance traffic to a suitable website; you can use a public website like www.zeus.com (remember to add a rule to set the host header if necessary). Check you can receive the website content through the ZXTM. Step 1: Create your Java ExtensionGo to the ZXTM 5.0 Admin interface, and locate the Catalog->Java Extensions page. On that page, locate the links to the Java Servlet API and ZXTM Java Extensions API files, and save these two Jar files in a convenient, long-term location:
In Eclipse, create a new project:
Once you've created the project, go to the Package Explorer. Right-click on your project and create a new Paste the following code into the
public class WaterMark extends HttpServlet {
private static final long serialVersionUID = 1L;
public void doGet( HttpServletRequest req, HttpServletResponse res )
throws ServletException, IOException
{
try {
ZXTMHttpServletResponse zres = (ZXTMHttpServletResponse) res;
String ct = zres.getHeader( "Content-Type" );
if( ct == null || ! ct.startsWith( "image/" ) ) return;
InputStream is = zres.getInputStream();
BufferedImage img = ImageIO.read( is );
Graphics2D g = (Graphics2D)img.getGraphics();
int width = img.getWidth();
int height = img.getHeight();
if( width < 200 || height < 30 ) return;
String message = "Hello world!";
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setComposite( AlphaComposite.getInstance(
AlphaComposite.SRC_OVER, (float)0.5 ));
Font myFont = new Font( "Sans", Font.BOLD, 18 );
Rectangle2D bb = myFont.getStringBounds( message,
g.getFontRenderContext() );
int x = 2;
int y = (int)bb.getHeight();
g.setFont( myFont );
g.setColor( Color.lightGray );
g.drawString( message, x, y );
zres.setHeader( "Content-Type", "image/png" );
ImageIO.write( img, "PNG", zres.getOutputStream() );
} catch( Exception e ) {
log( req.getRequestURI() + ": " + e.toString() );
}
}
public void doPost( HttpServletRequest req, HttpServletResponse res )
throws ServletException, IOException
{
doGet( req, res );
}
}
Paste this source in and hit ‘Ctrl-Shift-O’ to get the correct imports. Then save the file – this will automatically compile it; check that there were no errors in the compilation. Step 2: Load the extension into ZXTM and watermark some imagesGo to the Java Extensions catalog page and upload the Java Extension 'class' file for the
If the java code compiled correctly, you’ll find the class file in:
When you upload the class file, the ZXTM Admin Server will automatically create a simple RuleBuilder rule that invokes the Java Extension. Configure your virtual server to run the RuleBuilder rule on each response, then shift-reload the webpage that is delivered through ZXTM to clear your cache and reload each image:
Note the little "Hello world!" watermark on the top left of any images larger then 200x30 pixels. Step 3: Optimize the way that the extension is calledThe Java Extension is called by the RuleBuilder rule on every HTTP response. However, the extension only processes images; HTML, CSS and other document types are ignored. Selectively running Java ExtensionsInvoking a Java Extension carries some overhead, so it is prudent to ensure that Extensions are only invoked when they are needed. With a small change to the rule, you can ensure that this is the case. First, convert the "WaterMark" RuleBuilder rule to TrafficScript by editing the rule and using the "Convert Rule" button. This will create a simple TrafficScript rule which calls the WaterMark extension: java.run( "WaterMark" ); Edit the rule to add a condition that only runs the WaterMark extension when the object type is an image:
$contenttype = http.getResponseHeader( "Content-Type" );
if( string.startsWith( $contenttype, "image/" ) ) {
java.run( "WaterMark" );
}
Passing parameters to a Java ExtensionYou can pass parameters from TrafficScript to a Java Extension. They are passed in as additional arguments to the ‘java.run()’ TrafficScript function:
$ip = request.getRemoteIP();
$time = sys.timeToString( sys.time() );
$message = "IP: ".$ip.", ".$time;
$contenttype = http.getResponseHeader( "Content-Type" );
if( string.startsWith( $contenttype, "image/" ) ) {
java.run( "WaterMark", $message );
}
The Java extension can read these arguments using the 'args' attribute, which returns a string array of the argument values. Change the Extension code as follows: Originally: String message = "Hello world!"; Change to: String[] args = (String[])req.getAttribute( "args" ); String message = ( args != null ) ? args[0] : "Hello world!";
In Eclipse, you will need to re-save the source code after editing it; that will immediately compile it and update the Use the ZXTM Admin Interface to load in the new copy of the Java extension. Now, when you shift-reload the web page (to clear the cache) the watermark text on the image will contain the message created in the TrafficScript rule, with the IP address of the remote Of course, you could also generate this message directly in the Java Extension, but quick code changes (such as modifying the text in the message) are easier when the code resides in TrafficScript rather than a compiled Java class. Step 4: Live debugging and hot code patchingFinally, refer to the "Remote Debugging" section of the ZXTM 5.0 Java Development Guide. You can edit and save the code in Eclipse. When you save the code, Eclipse compiles it and patches the code in the JVM on the fly. Try changing the point size of the font or the color and see the effects immediately: Font myFont = new Font( "Serif", Font.BOLD, 12 ); and... g.setColor( Color.red ); Live patching in Eclipse is a great way to debug, test and update code, but remember that it only patches the code in the live Java VM. When ZXTM restarts, it will fall back to the version of the Java class that you originally uploaded, so once you’re finished, remember to upload the compiled class through the Admin interface so that it persists. Read more...Browse the Knowledgehub for more examples on ZXTM 5.0's Java Extensions.
Owen Garrett
[Zeus Dev Team] 22 May 2008
|
Recent Articles
Other Resources
|






