public synchronized void unbind(EndOid oid, Ticket ticket) {
   List<Subscriber> subscribers = routes.get(oid);
   if (subscribers == null) {
     System.err.println("NULL Subscribers for: " + oid.toString());
     return;
   }
   for (Subscriber sub : subscribers) {
     if (sub.ticket.equals(ticket)) {
       if (sub.continuation != null) {
         sub.continuation.resume();
         sub.continuation = null;
       }
       subscribers.remove(sub);
       break;
     }
   }
   routes.put(oid, subscribers);
   if (subscribers.size() == 0) {
     Endpoint ep = registry.get(oid);
     if (ep == null) {
       System.err.println("Cannot find endpont: " + oid.toString());
       return;
     }
     ep.idle = true;
     ep.idleTime = System.currentTimeMillis();
   }
 }
  protected synchronized void clean() {
    long now = System.currentTimeMillis();

    //		System.out.println("begin to clean: " + now);
    // clean subscribers
    Set<EndOid> keys = routes.keySet();
    for (EndOid key : keys) {
      List<Subscriber> subscribers = routes.get(key);
      Iterator<Subscriber> it = subscribers.iterator();
      while (it.hasNext()) {
        Subscriber sub = it.next();
        if ((sub.continuation == null || sub.continuation.isExpired())
            && ((sub.lastActive + 8000) < now)) {
          System.out.println("Clean NoTimeout Sub: " + sub);
          it.remove();
        } else if ((sub.lastActive + 28000) < now) {
          System.out.println("Clean Timeout Sub: " + sub);
          // TODO: when continuation cannot be expired
          if (sub.continuation != null && sub.continuation.isSuspended()) {
            sub.continuation.resume();
            sub.continuation = null;
          }
          it.remove();
        }
      }
      if (subscribers.size() == 0) {
        Endpoint ep = registry.get(key);
        if (ep != null && !ep.idle) {
          ep.idle = true;
          ep.idleTime = System.currentTimeMillis();
        }
      }
      routes.put(key, subscribers);
    }
    // clean endpoints
    Iterator<EndOid> keysIter = registry.keySet().iterator();

    //		for (EndOid key : keys) {
    while (keysIter.hasNext()) {
      EndOid key = keysIter.next();
      Endpoint ep = registry.get(key);
      if (ep.idle && (ep.idleTime + 8000) < now) {
        // System.out.println("Clean Endpoint: " + key);
        // presence
        List<Buddy> buddies = roster.buddies(key);
        for (Buddy b : buddies) {
          Presence p = new Presence("offline", key, b.fid);
          p.setNick(ep.nick);
          p.setShow("unavailable");
          p.setStatus(ep.status);
          // TODO:
          route(null, p);
        }
        // leave group presences
        for (EndOid grpOid : roster.groups(key)) {
          Presence p = new Presence("grpoffline", ep.endOid, grpOid);
          p.setNick(ep.nick);
          p.setShow("unavailable");
          p.setStatus(grpOid.name);
          route(null, p);
        }
        // remove
        //				registry.remove(key);
        keysIter.remove();
        routes.remove(key);
        roster.clean(key);
      }
    }
  }