W3C home > Mailing lists > Public > www-jigsaw@w3.org > July to August 1996

Resource class for yet another access count

From: albert (a.x.) zhou <azhou@nortel.ca>
Date: Thu, 29 Aug 1996 22:21:00 -0400
Message-ID: <"7402 Thu Aug 29 22:22:26 1996"@bnr.ca>
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 周晓华     | 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 GMT

This archive was generated by hypermail 2.2.0+W3C-0.50 : Monday, 9 April 2012 12:13:25 GMT