- From: albert (a.x.) zhou <azhou@nortel.ca>
- Date: Thu, 29 Aug 1996 22:21:00 -0400
- To: www-jigsaw@w3.org
I wrote a resource, called YAACResource, to generate an XBM image for
access counts (YAAC = Yet Another Access Count). Configuring
YAACResource should be easy and straightforward (see the class
description). Hope this is helpful and useful.
Critical comments are appreciated.
Cheers,
Albert
Albert X. Zhou ~{V\O~;*~} | Opinions expressed are my own, |
Nortel Technology | not necessarily those of Nortel. |
P.O. Box 3511, Station C, Ottawa, Canada K1Y 4H7
Tel: (613) 763-9702; Fax: (613) 765-4855; E-Mail: azhou@nortel.ca
// $RCSfile: YAACResource.java,v $$Revision: 1.5 $$Date: 96/08/25 23:11:47 $
package w3c.jigsaw.contrib;
import java.io.*;
import java.net.*;
import java.util.*;
import w3c.jigsaw.auth.*;
import w3c.jigsaw.http.*;
import w3c.jigsaw.resources.*;
/**
* This resource class generates an access count in the form of an XBM
* image for a configurable YAAC (yet another access count) tag embedded
* in an HTML page.
* <p>The steps to configure and use this graphical access counter are
* shown as follows:</p>
* <ol>
* <li>Configure the YAAC resource in a server directory, say the
* root. Attributes of the YAAC resource are rather self-
* explanatory. The name of this resource could be any string,
* eg, Yaac.
* <li>Put an IMG tag in the HTML file where you want to show the
* access count with the SRC value set to the URI where the YAAC
* resource has been configured above. For instance, if you add
* the YAAC resource with the name of Yaac to the root directory,
* the src should be set to "/Yaac".
* </ol>
* <p>The part of generating XBM image is based on a perl script by
* <a href="http://www.lerc.nasa.gov/people/OmarSyed/">Omar Syed</a>.</p>
* @version 0.13, 1996.08.26
* @author Albert Zhou, azhou@nortel.ca
*/
public class YAACResource extends HTTPResource {
protected static int ATTR_URLSTOCOUNT = -1; // URLs to count
protected static int ATTR_IPSTOIGNORE = -1; // IPs to ignore
protected static int ATTR_LEADINGZERO = -1; // show leading zeroes?
protected static int ATTR_INVERSE = -1; // inverse the counter image?
/**
* The yaac is the hashtable storing the access count values for
* those URLs registered via the YAAC resource.
*/
private static Hashtable yaac = new Hashtable();
/**
* The class variable digits stores the bitmaps for digits 0 to 9.
*/
private static byte[] digits = {
0x00,0x00,0x00,0x3c,0x66,0x66,0x66,0x66,
0x66,0x66,0x66,0x66,0x3c,0x00,0x00,0x00,
0x00,0x00,0x00,0x30,0x38,0x30,0x30,0x30,
0x30,0x30,0x30,0x30,0x30,0x00,0x00,0x00,
0x00,0x00,0x00,0x3c,0x66,0x60,0x60,0x30,
0x18,0x0c,0x06,0x06,0x7e,0x00,0x00,0x00,
0x00,0x00,0x00,0x3c,0x66,0x60,0x60,0x38,
0x60,0x60,0x60,0x66,0x3c,0x00,0x00,0x00,
0x00,0x00,0x00,0x30,0x30,0x38,0x38,0x34,
0x34,0x32,0x7e,0x30,0x78,0x00,0x00,0x00,
0x00,0x00,0x00,0x7e,0x06,0x06,0x06,0x3e,
0x60,0x60,0x60,0x66,0x3c,0x00,0x00,0x00,
0x00,0x00,0x00,0x38,0x0c,0x06,0x06,0x3e,
0x66,0x66,0x66,0x66,0x3c,0x00,0x00,0x00,
0x00,0x00,0x00,0x7e,0x66,0x60,0x60,0x30,
0x30,0x18,0x18,0x0c,0x0c,0x00,0x00,0x00,
0x00,0x00,0x00,0x3c,0x66,0x66,0x66,0x3c,
0x66,0x66,0x66,0x66,0x3c,0x00,0x00,0x00,
0x00,0x00,0x00,0x3c,0x66,0x66,0x66,0x66,
0x7c,0x60,0x60,0x30,0x1c,0x00,0x00,0x00
};
static {
Attribute a = null;
Class cls = null;
try {
cls = Class.forName("w3c.jigsaw.contrib.YAACResource");
}
catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
// declare/register the attributes
a = new StringArrayAttribute("URLstocount",
null,
Attribute.EDITABLE);
ATTR_URLSTOCOUNT = AttributeRegistery.registerAttribute(cls, a);
a = new IPTemplatesAttribute("IPstoignore",
null,
Attribute.EDITABLE);
ATTR_IPSTOIGNORE = AttributeRegistery.registerAttribute(cls, a);
a = new BooleanAttribute("Leadingzero",
Boolean.TRUE,
Attribute.EDITABLE);
ATTR_LEADINGZERO = AttributeRegistery.registerAttribute(cls, a);
a = new BooleanAttribute("Inverse",
Boolean.TRUE,
Attribute.EDITABLE);
ATTR_INVERSE = AttributeRegistery.registerAttribute(cls, a);
}
/**
* Get the list of resource URLs that are configured to count.
* @return the URL list (of strings).
*/
public String[] getURLs() {
return (String[]) getValue(ATTR_URLSTOCOUNT, null);
}
/**
* Get the list of IPs that should be ignored.
* @return the IP list.
*/
public short[][] getIPs() {
return (short[][]) getValue(ATTR_IPSTOIGNORE, null);
}
/**
* Get the leading zero boolean.
* @return the leading zero boolean.
*/
public boolean getLeadingZero() {
return getBoolean(ATTR_LEADINGZERO, true);
}
/**
* Get the inverse boolean.
* @return the inverse boolean.
*/
public boolean getInverse() {
return getBoolean(ATTR_INVERSE, true);
}
/**
* Get the access count for a desired URL.
* @return the access count.
*/
public synchronized int getCount(String url) {
int count = 0;
if (yaac.containsKey(url))
count = ((Integer)yaac.get(url)).intValue();
return count;
}
/**
* Set the access count for a desired URL.
*/
public synchronized void setCount(String url, int count) {
yaac.put(url, new Integer(count));
markModified(); // mark Yaac resource modified
}
/**
* Is the requested URL configured for YAAC count?
* @param url the requested URL
* @return true if the URL is YAAC configured; false otherwise.
*/
public boolean YAACConfigured(String url) {
boolean found = false;
String urls[] = getURLs();
if (urls != null) {
int i = 0;
while (!found && i < urls.length)
found = urls[i++].equals(url);
}
return found;
}
/**
* Should the request host be ignored regarding the YAAC count?
* @param ip the request host IP address in network bytes.
* @return true if the host should be ignored; false otherwise.
*/
public boolean hostToIgnore(byte[] ip) {
boolean found = false;
short ips[][] = getIPs();
if (ips != null) {
int i = 0;
while (!found && i < ips.length) {
found = true;
for (int j = 0; j < ip.length; j++) {
if (ips[i][j] != 256) // match/ignore the wild card
found &= (byte)ips[i][j] == ip[j];
}
i++;
}
}
return found;
}
/**
* Display the access count for the referer URL if it's configured.
* @param request the original request
* @exception HTTPException If processing fails.
*/
public Reply get(Request request) throws HTTPException
{
String referer = request.getField("referer");
if (YAACConfigured(referer)) {
int count = getCount(referer);
Client client = request.getClient();
InetAddress ipaddr = client.getInetAddress();
if (!hostToIgnore(ipaddr.getAddress()))
setCount(referer, ++count);
Reply reply = request.makeReply(HTTP.OK);
reply.setContentType("image/xbm");
String image = createYAACImage(count, getLeadingZero(), getInverse());
reply.setContent(image);
return reply;
}
else {
Reply error = request.makeReply(HTTP.NOT_ALLOWED);
error.setContentType("image/xbm");
throw new HTTPException(error);
}
}
/**
* Generate an XBM bitmap image for a YAAC access count.
* @param count the access count
* @param leadingzero the leading zeros desirable?
* @param inverse the inverse display desirable?
* @return the String containing the xbm image
*/
protected String createYAACImage(int count, boolean leadingzero, boolean inverse)
{
StringBuffer sb = new StringBuffer(Integer.toString(count));
int len = sb.length() > 7 ? sb.length() : 7;
if (leadingzero) {
int fill = len - sb.length();
for (int i = 0; i < fill; i++)
sb = sb.insert(0, '0');
}
StringBuffer image = new StringBuffer();
image.append("#define count_width " + len*8 + "\n");
image.append("#define count_height 16\n");
image.append("static char count_bits[] = {\n");
int digit;
for (int y = 0; y < 16; y++) {
for (int x = 0; x < len; x++) {
digit = digits[((sb.charAt(x) - '0') * 16) + y];
if (inverse) {
image.append(toHexString((((digit >> 4) ^ 0xf) & 0xf), ((digit ^ 0xf) & 0xf)));
}
else {
image.append(toHexString(((digit >> 4) & 0xf), (digit & 0xf)));
}
if (x < len-1)
image.append(',');
}
image.append((y == 15) ? "};" : ",");
image.append('\n');
}
return image.toString();
}
/**
* Convert an integer with high and low bytes to its equivalent Hex
* representation starting with the hex indicator '0x'.
* @param hi the high byte
* @param low the low byte
* @return the String containing the Hex string
*/
private String toHexString(int hi, int low) {
return "0x" + Integer.toString(hi << 4 | low, 16);
}
/**
* Convert an integer to its equivalent Hex representation starting
* with the hex indicator '0x'.
* @param i the integer
* @return the String containing the Hex string
*/
private String toHexString(int i) {
return "0x" + Integer.toHexString(i);
}
/**
* Pickle a YAAC resource along with its associated counts.
* @param out the data output stream to pickle to.
*/
public synchronized void pickle(DataOutputStream out) throws IOException
{
super.pickle(out);
// pickle the yaac counts
if (yaac.isEmpty()) {
out.writeInt(0);
}
else {
out.writeInt(yaac.size());
Enumeration urls = yaac.keys();
String url;
int count;
while (urls.hasMoreElements()) {
url = (String) urls.nextElement();
count = getCount(url);
out.writeUTF(url);
out.writeInt(count);
}
}
}
/**
* Unpickle a YAAC resource.
* @param in the input stream to unpickle from.
* @param defs the default attributes for the unpickled resource.
*/
public AttributeHolder unpickleInstance(DataInputStream in, Hashtable defs) throws IOException
{
super.unpickleInstance(in, defs);
// unpickle the yaac counts
int size = in.readInt();
if (size > 0) {
yaac = new Hashtable();
String url;
int count;
for (int i = 0; i < size; i++) {
url = in.readUTF();
count = in.readInt();
setCount(url, count);
}
}
return this;
}
}
Received on Thursday, 29 August 1996 22:23:13 UTC