/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * limitations under the License. */ /** * @author Alexei Y. Zakharov */ package org.apache.harmony.jndi.provider.dns; import java.util.Enumeration; import java.util.HashMap; import java.util.Vector; /** * A cache for received resource records. Common for all active resolvers. * * TODO handling of records with TTL set to 0; should not be cached. */ class ResolverCache { /** keys - zone & host names; values - vectors with RRs */ HashMap<String, Vector<CacheEntry>> names = new HashMap<String, Vector<CacheEntry>>(); /** * Since <code>ResolverCache</code> is singleton class its constructor * should be hidden. */ private ResolverCache() { names = new HashMap<String, Vector<CacheEntry>>(); } private static ResolverCache instance = null; /** * <code>ResolverCache</code> is a singleton class. * * @return active instance of <code>ResolverCache</code> */ static ResolverCache getInstance() { if (instance == null) { instance = new ResolverCache(); } return instance; } /** * Looks through the cache and returns all suitable resource records * * @param question * a question record that determines which records we want to get * from the cache * @return Enumeration of found Resource Records. */ synchronized Enumeration<ResourceRecord> get(QuestionRecord question) { String name = question.getQName().toLowerCase(); Vector<CacheEntry> vect = names.get(name); int qClass = question.getQClass(); int qType = question.getQType(); Vector<ResourceRecord> resVect = new Vector<ResourceRecord>(); if (vect != null) { for (int i = 0; i < vect.size(); i++) { CacheEntry curEntry = vect.elementAt(i); ResourceRecord curRR = curEntry.getRR(); if (curEntry.getBestBefore() < System.currentTimeMillis()) { // the record is out of date vect.removeElementAt(i--); continue; } if (qClass == ProviderConstants.ANY_QCLASS || qClass != curRR.getRRClass()) { continue; } if (qType == ProviderConstants.ANY_QTYPE || qType != curRR.getRRType()) { continue; } resVect.addElement(curRR); } } return resVect.elements(); } /** * Puts element into the cache. Doesn't put records with zero TTLs. Doesn't * put records with bad TTLs. * * @param record * a resource record to insert */ synchronized void put(ResourceRecord record) { String name = record.getName().toLowerCase(); Vector<CacheEntry> vect = names.get(name); long curTime = System.currentTimeMillis(); CacheEntry entry = null; if (vect == null) { vect = new Vector<CacheEntry>(); names.put(name, vect); } // TTL should be between 0 and 2^31; if greater - should be set to 0 // See RFC 2181 point 8 if (record.getTtl() >> 31 != 0) { record.setTtl(0); } // skip records with wildcards in names or with zero TTL if (record.getTtl() > 0 && (record.getName().indexOf('*') == -1)) { entry = new CacheEntry(record, curTime + record.getTtl()); // remove old occurrence if any for (int i = 0; i < vect.size(); i++) { CacheEntry exEntry = vect.elementAt(i); ResourceRecord exRec = exEntry.rr; if (ProviderMgr .namesAreEqual(record.getName(), exRec.getName()) && record.getRRClass() == exRec.getRRClass() && record.getRRType() == exRec.getRRType()) { if (record.getRData() != null && exRec.getRData() != null && record.getRData().equals(exRec.getRData())) { vect.remove(i); break; } } } vect.addElement(entry); } } /** * Removes all cached entries. */ synchronized void clear() { names = new HashMap<String, Vector<CacheEntry>>(); } /** * Represents SLIST cache entry. */ static class CacheEntry { private ResourceRecord rr; private long bestBefore; /** * Constructs new cache entry. * * @param rr * Resource Record * @param bestBefore * best before (time in millis) */ public CacheEntry(ResourceRecord rr, long bestBefore) { this.rr = rr; this.bestBefore = bestBefore; } /** * @return Returns the bestBefore. */ public long getBestBefore() { return bestBefore; } /** * @return Returns the Resource Record. */ public ResourceRecord getRR() { return rr; } } }