/*
* 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.
*/
package org.apache.harmony.jndi.provider.dns;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import org.apache.harmony.jndi.internal.nls.Messages;
/**
* Represents DNS resolver's SLIST - the structure to keep the collected
* information about active DNS servers and zones they contain information
* about.
*
* @see RFC 1034 TODO some methods can be optimized
*/
class SList {
public static int NETWORK_FAILURE = Integer.MAX_VALUE - 3;
public static int TIMEOUT = Integer.MAX_VALUE - 2;
public static int SERVER_FAILURE = Integer.MAX_VALUE - 1;
public static int UNKNOWN = 0;
// Hash with vectors; one vector of server entries per zone
private Hashtable<String, Vector<Entry>> zones;
// the array with known DNS servers information
private Vector<Server> servers;
/**
* @see #getInstance()
*/
private SList() {
zones = new Hashtable<String, Vector<Entry>>();
servers = new Vector<Server>();
}
private static SList instance = new SList();
/**
* <code>SList</code> is a singleton class.
*
* @return instance of <code>SList</code>
*/
static SList getInstance() {
return instance;
}
/**
* Updates existent SLIST entry or creates a new one. S-List will be sorted
* according the response time. Entries with bigger response will be placed
* father from the beginning of the list.
*
* @param zone
* the name of DNS zone
* @param server
* the server that is known to have the information about given
* zone
* @param responseTime
* response time for server for this particular DNS zone
*/
void updateEntry(String zone, Server server, int responseTime) {
String normZoneName = ProviderMgr.normalizeName(zone);
Vector<Entry> vect;
Entry entryToAdd = new Entry(normZoneName, getServerNum(server),
responseTime);
synchronized (zones) {
vect = zones.get(ProviderMgr.normalizeName(normZoneName));
if (vect == null) {
vect = new Vector<Entry>();
vect.addElement(entryToAdd);
zones.put(normZoneName, vect);
} else {
boolean added = false;
// delete previous occurrence of given server
for (int i = 0; i < vect.size(); i++) {
Entry curEntry = vect.elementAt(i);
if (server.equals(serverAtNum(curEntry.getServerNum()))) {
vect.removeElementAt(i);
break;
}
}
// and insert a new one with updated response time
for (int i = 0; i < vect.size(); i++) {
Entry curEntry = vect.elementAt(i);
if (responseTime < curEntry.getResponseTime()) {
vect.insertElementAt(entryToAdd, i);
added = true;
break;
}
}
// append to the end of list if not found
if (!added) {
vect.addElement(entryToAdd);
}
}
} // synchronized block
}
/**
* Returns the best guess about that DNS server should be chosen to send the
* request concerning the particular DNS zone.
*
* @param zone
* the name of DNS zone
* @return best guess - a <code>SList.Server</code> object;
* <code>null</code> if the information is not found
*/
Server getBestGuess(String zone, Hashtable<Server, ?> serversToIgnore) {
Vector<Entry> vect;
synchronized (zones) {
vect = zones.get(ProviderMgr.normalizeName(zone));
if (vect != null && vect.size() > 0) {
for (int i = 0; i < vect.size(); i++) {
Entry entry = vect.elementAt(i);
if (serversToIgnore != null) {
if (serversToIgnore.get(serverAtNum(entry
.getServerNum())) != null) {
continue;
}
}
return serverAtNum(entry.getServerNum());
}
}
}
return null;
}
/**
* Removes occurrence of given server related to given zone from the SLIST.
*
* @param zone
* DNS zone
* @param server
* the server to remove
*/
void dropServer(String zone, Server server) {
Vector<Entry> vect;
synchronized (zones) {
vect = zones.get(ProviderMgr.normalizeName(zone));
if (vect != null) {
for (int i = 0; i < vect.size(); i++) {
Entry entry = vect.elementAt(i);
if (server.equals(serverAtNum(entry.getServerNum()))) {
vect.removeElementAt(i);
break;
}
}
}
}
}
/**
* @param zone
* the name of zone
* @param server
* DNS server
* @return <code>true</code> if SList has information about specified
* server & zone combination; <code>false</code> otherwise
*/
boolean hasServer(String zone, Server server) {
Vector<Entry> vect;
synchronized (zones) {
vect = zones.get(ProviderMgr.normalizeName(zone));
if (vect != null) {
for (int i = 0; i < vect.size(); i++) {
Entry entry = vect.elementAt(i);
if (server.equals(serverAtNum(entry.getServerNum()))) {
return true;
}
}
}
}
return false;
}
/**
* @param zone
* the name DNS zone
* @param srvName
* the name of the server
* @param srvPort
* the port of the server
* @return <code>Server</code> object with specified attributes
*/
Server getServerByName(String zone, String name, int port) {
Vector<Entry> vect;
synchronized (zones) {
vect = zones.get(ProviderMgr.normalizeName(zone));
if (vect != null) {
for (int i = 0; i < vect.size(); i++) {
Entry entry = vect.elementAt(i);
if (ProviderMgr.namesAreEqual(name, serverAtNum(
entry.getServerNum()).getName())
&& port == serverAtNum(entry.getServerNum())
.getPort()) {
return serverAtNum(entry.getServerNum());
}
}
}
}
return null;
}
/**
* @param zone
* name of DNS zone
* @param srvIP
* IPv4 address of server
* @param srvPort
* port on server
* @return <code>Server</code> object with specified attributes
*/
Server getServerByIP(String zone, String ip, int port) {
Vector<Entry> vect;
synchronized (zones) {
vect = zones.get(ProviderMgr.normalizeName(zone));
if (vect != null) {
for (int i = 0; i < vect.size(); i++) {
Entry entry = vect.elementAt(i);
if (ip.equals(serverAtNum(entry.getServerNum()).getIP())
&& port == serverAtNum(entry.getServerNum())
.getPort()) {
return serverAtNum(entry.getServerNum());
}
}
}
}
return null;
}
/**
* @param zone
* the name of DNS zone to query SLIST with
* @param server
* the server to compare with
* @return first <code>Server</code> object from SLIST that equals to
* specified server in terms of <code>equals()</code> method;
* <code>null</code> if not found.
* @see Server#equals(Server)
*/
Server getServerByServer(String zone, Server server) {
Vector<Entry> vect;
synchronized (zones) {
vect = zones.get(ProviderMgr.normalizeName(zone));
if (vect != null) {
for (int i = 0; i < vect.size(); i++) {
Entry entry = vect.elementAt(i);
if (server.equals(serverAtNum(entry.getServerNum()))) {
return serverAtNum(entry.getServerNum());
}
}
}
}
return null;
}
// --- managing local list of servers ---
// since the list of servers is add-only entity a write synchronization
// should be enough
/**
* @return number of given server in the internal array of servers; add the
* server if not found
*/
private int getServerNum(Server server) {
if (servers.contains(server)) {
return servers.indexOf(server);
}
synchronized (servers) {
servers.addElement(server);
return servers.size() - 1;
}
}
/**
* @param num
* internal number of server
* @return <code>Server</code> object found at specified index
*/
private Server serverAtNum(int num) {
if (num < servers.size()) {
return servers.elementAt(num);
}
return null;
}
/**
* Checks if given server is present in the internal list of known servers.
*
* @param hostname
* host name of server
* @return <code>true</code> or <code>false</code>
*/
boolean hasServer(String hostname) {
return servers.contains(new Server(hostname, null,
ProviderConstants.DEFAULT_DNS_PORT));
}
/**
* Returns all occurrences of server with specified
*
* @param name
* hostname
* @return found server object or <code>null</code> if not found
*/
Enumeration<Server> getServersByName(String name) {
Vector<Server> result = new Vector<Server>();
if (name == null) {
// jndi.34=hostname is null
throw new NullPointerException(Messages.getString("jndi.34")); //$NON-NLS-1$
}
for (int i = 0; i < servers.size(); i++) {
Server curServ = servers.get(i);
if (curServ.getName() != null
&& ProviderMgr.namesAreEqual(name, curServ.getName())) {
result.addElement(curServ);
}
}
return result.elements();
}
/**
* Add IP information of server in list. Affects only servers with IP set to
* <code>null</code>.
*
* @param hostname
* hostname of server
* @param newIP
* new IP
*/
void setServerIP(String hostname, String newIP) {
String nameNorm = ProviderMgr.normalizeName(hostname);
for (int i = 0; i < servers.size(); i++) {
Server serv = servers.elementAt(i);
if (nameNorm.equals(serv.getName()) && serv.getIP() == null) {
serv.setIP(newIP);
break;
}
}
}
// --- additional classes ---
/**
* Represents an SLIST entry.
*/
static class Entry {
private String zoneName;
private int serverNum;
private int responseTime;
/**
* Creates new SLIST entry.
*
* @param zoneName
* @param server
* @param respTime
*/
public Entry(String zoneName, int serverNum, int respTime) {
this.zoneName = zoneName;
this.serverNum = serverNum;
this.responseTime = respTime;
}
/**
* @return Returns the responseTime.
*/
public int getResponseTime() {
return responseTime;
}
/**
* @param responseTime
* The responseTime to set.
*/
public void setResponseTime(int responseTime) {
this.responseTime = responseTime;
}
/**
* @return Returns the server.
*/
public int getServerNum() {
return serverNum;
}
/**
* @param server
* The server to set.
*/
public void setServerNum(int serverNum) {
this.serverNum = serverNum;
}
/**
* @return Returns the zoneName.
*/
public String getZoneName() {
return zoneName;
}
/**
* @param zoneName
* The zoneName to set.
*/
public void setZoneName(String zoneName) {
this.zoneName = zoneName;
}
}
/**
* Represents a DNS server.
*/
static class Server {
private String serverName;
private String serverIP;
private int serverPort;
/**
* Constructs new <code>Server</code> object with given parameters.
*
* @param serverName
* the name of the server
* @param serverIP
* IP address of the server
* @param serverPort
* a port number
*/
public Server(String serverName, String serverIP, int serverPort) {
this.serverName = ProviderMgr.normalizeName(serverName);
this.serverIP = serverIP;
this.serverPort = serverPort;
}
/**
* Returns <code>true</code> if two servers are equal,
* <code>false</code> otherwise.
*
* @param obj
* a <code>Server</code> object to compare with
* @return <code>true</code> or <code>false</code>
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Server)) {
return false;
}
Server srv = (Server) obj;
if (serverIP == null || srv.getIP() == null) {
if (this.getName() == null || srv.getName() == null) {
return false;
}
return ProviderMgr.namesAreEqual(this.getName(), srv.getName())
&& this.getPort() == srv.getPort();
}
return this.getIP().equals(srv.getIP())
&& this.getPort() == srv.getPort();
}
/**
* Returns the hash code of the receiver.
*/
@Override
public int hashCode() {
return getIP().hashCode() + getPort();
}
/**
* @return Returns the serverIP.
*/
public String getIP() {
return serverIP;
}
/**
* @return Returns the serverName.
*/
public String getName() {
return serverName;
}
/**
* @return Returns the serverPort.
*/
public int getPort() {
return serverPort;
}
/**
* @param serverIP
* The serverIP to set.
*/
public void setIP(String serverIP) {
this.serverIP = serverIP;
}
/**
* @param serverName
* The serverName to set.
*/
public void setName(String serverName) {
this.serverName = ProviderMgr.normalizeName(serverName);
}
/**
* @param serverPort
* The serverPort to set.
*/
public void setPort(int serverPort) {
this.serverPort = serverPort;
}
@Override
public String toString() {
if (this.serverName != null) {
return serverName + ":" + serverPort; //$NON-NLS-1$
}
return serverIP + ":" + serverPort; //$NON-NLS-1$
}
}
}