class SetBasedUriUniqFilter implements UriUniqFilter, Serializable {
    private static Logger LOGGER =
        Logger.getLogger(SetBasedUriUniqFilter.class.getName());

    protected CrawlUriReceiver receiver;
    protected PrintWriter profileLog;
    protected long duplicateCount = 0;
    protected long duplicatesAtLastSample = 0;
    protected AtomicLong addedCount = new AtomicLong();
    
    public SetBasedUriUniqFilter() {
        super();
        String profileLogFile = 
            System.getProperty(SetBasedUriUniqFilter.class.getName()
                + ".profileLogFile");
        if (profileLogFile != null) {
            setProfileLog(new File(profileLogFile));
        }
    }
    
    protected abstract boolean setAdd(CharSequence key);

    protected abstract boolean setRemove(CharSequence key);

    protected abstract long setCount();
    
    @Override
    public long addedCount() {
        return addedCount.get();
    }

    public long count() {
        return setCount();
    }

    public long pending() {
        // no items pile up in this implementation
        return 0;
    }

    public void setDestination(CrawlUriReceiver receiver) {
        this.receiver = receiver;
    }

    protected void profileLog(String key) {
        if (profileLog != null) {
            profileLog.println(key);
        }
    }
    
    public void add(String key, CrawlURI value) {<FILL_FUNCTION_BODY>}

    public void addNow(String key, CrawlURI value) {
        add(key, value);
    }
    
    public void addForce(String key, CrawlURI value) {
        profileLog(key);
        setAdd(key);
        this.receiver.receive(value);
    }

    public void note(String key) {
        profileLog(key);
        setAdd(key);
    }

    public void forget(String key, CrawlURI value) {
        setRemove(key);
    }

    public long requestFlush() {
        // unnecessary; all actions with set-based uniqfilter are immediate
        return 0;
    }

    public void close() {
        if (profileLog != null) {
            profileLog.close();
        }
    }

    public void setProfileLog(File logfile) {
        try {
            profileLog = new PrintWriter(new BufferedOutputStream(
                    new FileOutputStream(logfile)));
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        }
    }
}

        addedCount.incrementAndGet();
        profileLog(key);
        if (setAdd(key)) {
            this.receiver.receive(value);
            if (setCount() % 50000 == 0) {
                LOGGER.log(Level.FINE, "count: " + setCount() + " totalDups: "
                        + duplicateCount + " recentDups: "
                        + (duplicateCount - duplicatesAtLastSample));
                duplicatesAtLastSample = duplicateCount;
            }
        } else {
            duplicateCount++;
        }
    
<no_super_class>