* * As an example, a lookup for "example.com" may return "im.example.com:5269". * * @param domain the domain. * @return a HostAddress, which encompasses the hostname and port that the XMPP * server can be reached at for the specified domain. */ public static HostAddress resolveXMPPDomain(String domain) { if (context == null) { return new HostAddress(domain, 5222); } String key = "c" + domain; // Return item from cache if it exists. if (cache.containsKey(key)) { HostAddress address = (HostAddress)cache.get(key); if (address != null) { return address; } } String host = domain; int port = 5222; try { Attributes dnsLookup = context.getAttributes("_xmpp-client._tcp." + domain, new String[]{"SRV"}); String srvRecord = (String)dnsLookup.get("SRV").get(); String [] srvRecordEntries = srvRecord.split(" "); port = Integer.parseInt(srvRecordEntries[srvRecordEntries.length-2]); host = srvRecordEntries[srvRecordEntries.length-1]; } catch (Exception e) { // Ignore. } // Host entries in DNS should end with a ".". if (host.endsWith(".")) { host = host.substring(0, host.length()-1); } HostAddress address = new HostAddress(host, port); // Add item to cache. cache.put(key, address); return address; } /** * Returns the host name and port that the specified XMPP server can be * reached at for server-to-server communication. A DNS lookup for a SRV * record in the form "_xmpp-server._tcp.example.com" is attempted, according * to section 14.4 of RFC 3920. If that lookup fails, a lookup in the older form * of "_jabber._tcp.example.com" is attempted since servers that implement an * older version of the protocol may be listed using that notation. If that * lookup fails as well, it's assumed that the XMPP server lives at the * host resolved by a DNS lookup at the specified domain on the default port * of 5269.
* * As an example, a lookup for "example.com" may return "im.example.com:5269". * * @param domain the domain. * @return a HostAddress, which encompasses the hostname and port that the XMPP * server can be reached at for the specified domain. */ public static HostAddress resolveXMPPServerDomain(String domain) { if (context == null) { return new HostAddress(domain, 5269); } String key = "s" + domain; // Return item from cache if it exists. if (cache.containsKey(key)) { HostAddress address = (HostAddress)cache.get(key); if (address != null) { return address; } } String host = domain; int port = 5269; try { Attributes dnsLookup = context.getAttributes("_xmpp-server._tcp." + domain, new String[]{"SRV"}); String srvRecord = (String)dnsLookup.get("SRV").get(); String [] srvRecordEntries = srvRecord.split(" "); port = Integer.parseInt(srvRecordEntries[srvRecordEntries.length-2]); host = srvRecordEntries[srvRecordEntries.length-1]; } catch (Exception e) { // Attempt lookup with older "jabber" name. try { Attributes dnsLookup = context.getAttributes("_jabber._tcp." + domain, new String[]{"SRV"}); String srvRecord = (String)dnsLookup.get("SRV").get(); String [] srvRecordEntries = srvRecord.split(" "); port = Integer.parseInt(srvRecordEntries[srvRecordEntries.length-2]); host = srvRecordEntries[srvRecordEntries.length-1]; } catch (Exception e2) { // Ignore. } } // Host entries in DNS should end with a ".". if (host.endsWith(".")) { host = host.substring(0, host.length()-1); } HostAddress address = new HostAddress(host, port); // Add item to cache. cache.put(key, address); return address; } /** * Encapsulates a hostname and port. */ public static class HostAddress { private String host; private int port; private HostAddress(String host, int port) { this.host = host; this.port = port; } /** * Returns the hostname. * * @return the hostname. */ public String getHost() { return host; } /** * Returns the port. * * @return the port. */ public int getPort() { return port; } public String toString() { return host + ":" + port; } public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof HostAddress)) { return false; } final HostAddress address = (HostAddress) o; if (!host.equals(address.host)) { return false; } return port == address.port; } } } /** * A specialized Map that is size-limited (using an LRU algorithm) and * has an optional expiration time for cache items. The Map is thread-safe.
* * The algorithm for cache is as follows: a HashMap is maintained for fast * object lookup. Two linked lists are maintained: one keeps objects in the * order they are accessed from cache, the other keeps objects in the order * they were originally added to cache. When objects are added to cache, they * are first wrapped by a CacheObject which maintains the following pieces * of information:
*
* Keeping track of cache hits and misses lets one measure how efficient
* the cache is; the higher the percentage of hits, the more efficient.
*/
protected long cacheHits, cacheMisses = 0L;
/**
* Create a new cache and specify the maximum size of for the cache in
* bytes, and the maximum lifetime of objects.
*
* @param maxSize the maximum number of objects the cache will hold. -1
* means the cache has no max size.
* @param maxLifetime the maximum amount of time (in ms) objects can exist in
* cache before being deleted. -1 means objects never expire.
*/
public Cache(int maxSize, long maxLifetime) {
if (maxSize == 0) {
throw new IllegalArgumentException("Max cache size cannot be 0.");
}
this.maxCacheSize = maxSize;
this.maxLifetime = maxLifetime;
// Our primary data structure is a hash map. The default capacity of 11
// is too small in almost all cases, so we set it bigger.
map = new HashMap
*/
private static class LinkedList {
/**
* The root of the list keeps a reference to both the first and last
* elements of the list.
*/
private LinkedListNode head = new LinkedListNode("head", null, null);
/**
* Creates a new linked list.
*/
public LinkedList() {
head.next = head.previous = head;
}
/**
* Returns the first linked list node in the list.
*
* @return the first element of the list.
*/
public LinkedListNode getFirst() {
LinkedListNode node = head.next;
if (node == head) {
return null;
}
return node;
}
/**
* Returns the last linked list node in the list.
*
* @return the last element of the list.
*/
public LinkedListNode getLast() {
LinkedListNode node = head.previous;
if (node == head) {
return null;
}
return node;
}
/**
* Adds a node to the beginning of the list.
*
* @param node the node to add to the beginning of the list.
* @return the node
*/
public LinkedListNode addFirst(LinkedListNode node) {
node.next = head.next;
node.previous = head;
node.previous.next = node;
node.next.previous = node;
return node;
}
/**
* Adds an object to the beginning of the list by automatically creating a
* a new node and adding it to the beginning of the list.
*
* @param object the object to add to the beginning of the list.
* @return the node created to wrap the object.
*/
public LinkedListNode addFirst(Object object) {
LinkedListNode node = new LinkedListNode(object, head.next, head);
node.previous.next = node;
node.next.previous = node;
return node;
}
/**
* Adds an object to the end of the list by automatically creating a
* a new node and adding it to the end of the list.
*
* @param object the object to add to the end of the list.
* @return the node created to wrap the object.
*/
public LinkedListNode addLast(Object object) {
LinkedListNode node = new LinkedListNode(object, head, head.previous);
node.previous.next = node;
node.next.previous = node;
return node;
}
/**
* Erases all elements in the list and re-initializes it.
*/
public void clear() {
//Remove all references in the list.
LinkedListNode node = getLast();
while (node != null) {
node.remove();
node = getLast();
}
//Re-initialize.
head.next = head.previous = head;
}
/**
* Returns a String representation of the linked list with a comma
* delimited list of all the elements in the list.
*
* @return a String representation of the LinkedList.
*/
public String toString() {
LinkedListNode node = head.next;
StringBuilder buf = new StringBuilder();
while (node != head) {
buf.append(node.toString()).append(", ");
node = node.next;
}
return buf.toString();
}
}
/**
* Doubly linked node in a LinkedList. Most LinkedList implementations keep the
* equivalent of this class private. We make it public so that references
* to each node in the list can be maintained externally.
*
* Exposing this class lets us make remove operations very fast. Remove is
* built into this class and only requires two reference reassignments. If
* remove existed in the main LinkedList class, a linear scan would have to
* be performed to find the correct node to delete.
*
* The linked list implementation was specifically written for the Jive
* cache system. While it can be used as a general purpose linked list, for
* most applications, it is more suitable to use the linked list that is part
* of the Java Collections package.
*/
private static class LinkedListNode {
public LinkedListNode previous;
public LinkedListNode next;
public Object object;
/**
* This class is further customized for the Jive cache system. It
* maintains a timestamp of when a Cacheable object was first added to
* cache. Timestamps are stored as long values and represent the number
* of milliseconds passed since January 1, 1970 00:00:00.000 GMT.
*
* The creation timestamp is used in the case that the cache has a
* maximum lifetime set. In that case, when
* [current time] - [creation time] > [max lifetime], the object will be
* deleted from cache.
*/
public long timestamp;
/**
* Constructs a new linked list node.
*
* @param object the Object that the node represents.
* @param next a reference to the next LinkedListNode in the list.
* @param previous a reference to the previous LinkedListNode in the list.
*/
public LinkedListNode(Object object, LinkedListNode next,
LinkedListNode previous)
{
this.object = object;
this.next = next;
this.previous = previous;
}
/**
* Removes this node from the linked list that it is a part of.
*/
public void remove() {
previous.next = next;
next.previous = previous;
}
/**
* Returns a String representation of the linked list node by calling the
* toString method of the node's object.
*
* @return a String representation of the LinkedListNode.
*/
public String toString() {
return object.toString();
}
}
}
//GenericsNote: Converted.
/*
* Copyright 2003-2004 The Apache Software Foundation
*
* Licensed 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.
*/
/**
* Abstract Pair class to assist with creating correct Map Entry implementations.
*
* @author James Strachan
* @author Michael A. Smith
* @author Neil O'Toole
* @author Matt Hall, John Watkinson, Stephen Colebourne
* @version $Revision: 1.1 $ $Date: 2005/10/11 17:05:32 $
* @since Commons Collections 3.0
*/
abstract class AbstractMapEntry