private Document getXmlResource(int aid) throws Exception { synchronized (BANNED) { CachedResource resource = getResourceCache().xml(aid, this::getResource).fetch(withPermit(fetchIfModified(), r -> { if (BANNED.get()) { throw new IllegalStateException("AniDB has already banned your IP. Please stop hitting AniDB for at least 24 hours."); } if (HARD_LIMIT.availablePermits() == 0) { log.warning("AniDB HTTP API daily limit has been exceeded. Processing will continue in 24 hours..."); Cache.DISK_STORE.flush(); TimeUnit.HOURS.sleep(24); } debug.finest(message("AniDB daily limit", HARD_LIMIT)); HARD_LIMIT.acquirePermit(); debug.finest(message("AniDB minutely limit", SOFT_LIMIT)); SOFT_LIMIT.acquirePermit(); })).expire(Cache.ONE_WEEK); // disable weekly expiration if ((BANNED.get() || HARD_LIMIT.availablePermits() < 100) && resource.available()) { log.config(message("AniDB cache expiration temporarily disabled due to heavy usage", aid, HARD_LIMIT)); resource.expire(Cache.NEVER); } // retrieve resource Document dom = resource.get(); // check for errors (e.g. Banned) String error = selectString("/error", dom); if (error != null && !error.isEmpty()) { // client error (cache expected response) if ("anime not found".equalsIgnoreCase(error)) { throw new LookupException(error, aid); } // server errors (do not cache unexpected response) resource.invalidate(); if ("banned".equalsIgnoreCase(error)) { BANNED.set(true); throw new InvalidResponseException("AniDB has banned your IP due to excessive usage. Please try again in 24 hours or later."); } // unknown error throw new InvalidResponseException(error); } return dom; } }