stasis/je-7.5.11/test/com/sleepycat/rep/utilint/LocalAliasNameService.java
2019-06-26 14:22:56 -04:00

238 lines
8.5 KiB
Java
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*-
* Copyright (C) 2002, 2017, Oracle and/or its affiliates. All rights reserved.
*
* This file was distributed by Oracle as part of a version of Oracle Berkeley
* DB Java Edition made available at:
*
* http://www.oracle.com/technetwork/database/database-technologies/berkeleydb/downloads/index.html
*
* Please see the LICENSE file included in the top-level directory of the
* appropriate version of Oracle Berkeley DB Java Edition for a copy of the
* license and additional information.
*/
package com.sleepycat.je.rep.utilint;
import java.lang.reflect.Field;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.logging.Logger;
import sun.net.spi.nameservice.NameService;
import sun.net.spi.nameservice.NameServiceDescriptor;
import com.sleepycat.je.utilint.LoggerUtils;
/**
* Define a JDK name service provider that can be controlled by tests to
* simulate DNS failures. The idea is to define dummy DNS names that translate
* to the loopback address, and then undefine them as needed.
*
* <p>To use this class, you need to make a few modifications to the JVM that
* wants to use it: <ul>
*
* <li>Tell the JDK how to find this service provider by creating a resource,
* available in the class path, named
* <code>META-INF/services/sun.net.spi.nameservice.NameService</code>, which
* contains the fully qualified name of the {@link Descriptor} class. You can
* do that by create a file with that pathname relative to a component of the
* classpath.
*
* <li>Add the new name service provider as a second provider after the
* standard provider by setting the following system properties:
* <pre>
* -Dsun.net.spi.nameservice.provider.1=dns,default
* -Dsun.net.spi.nameservice.provider.2=dns,localalias
* </pre>
* Note that these properties need to be set on the command line so they are
* available at JVM startup time.
*
* <li>Disable the DNS cache so that changes made to this provider will take
* effect. Do this by calling {@link #setDNSCachePolicy} with 0 for both cache
* policies, and reverting to the original values afterwards.
*
* </ul>
*
* Although the name service provider facility is undocumented, at last check
* it appears to be supported by the J9 and icedtea JVM implementations.
*/
public class LocalAliasNameService implements NameService {
static final Logger logger =
LoggerUtils.getLoggerFixedPrefix(LocalAliasNameService.class, "Test");
/* Only referenced by getLocalHost */
private static InetAddress localHost = null;
private static boolean computingLocalHost = false;
/**
* The service descriptor that defines {@code LocalAliasNameService} as a
* "dns" provider named "localalias".
*/
public static class Descriptor implements NameServiceDescriptor {
@Override
public NameService createNameService() {
return new LocalAliasNameService();
}
@Override
public String getProviderName() {
return "localalias";
}
@Override
public String getType() {
return "dns";
}
}
private static final Set<String> aliases =
Collections.synchronizedSet(new HashSet<String>());
private LocalAliasNameService() {
logger.info("Created LocalAliasNameService");
}
/**
* Add a new alias for the loopback address.
*
* @param alias the new alias
*/
public static void addAlias(String alias) {
logger.info("LocalAliasNameService.addAlias: " + alias);
aliases.add(alias);
}
/**
* Remove an alias for the loopback address.
*
* @param alias the alias to remove
*/
public static void removeAlias(String alias) {
logger.info("LocalAliasNameService.removeAlias: " + alias);
aliases.remove(alias);
}
/**
* Remove all aliases for the loopback address.
*/
public static void clearAllAliases() {
logger.info("LocalAliasNameService.clearAllAliases");
aliases.clear();
}
/**
* Use reflection to set the internal DNS cache policy. The {@link
* InetAddress} class documents security properties for controlling this
* (networkaddress.cache.ttl and networkaddress.cache.negative.ttl), but
* those settings only take effect when they are specified before any
* address lookups are performed. The JUnit test infrastructure does
* address lookups before testing starts, so the security properties are
* not effective. Instead, use reflection to set the field values
* directly.
*
* <p>The cache policy values specify the time in milliseconds that the
* cache should remain valid, with 0 meaning don't cache and -1 meaning
* cache stays valid forever.
*
* @param positive the cache policy for successful lookups
* @param negative the cache policy for unsuccessful lookups
* @return an array of the previous cache policies, with the positive cache
* value appearing first
*/
public static int[] setDNSCachePolicy(int positive, int negative) {
try {
Class<?> cachePolicyClass
= Class.forName("sun.net.InetAddressCachePolicy");
Field positiveField =
cachePolicyClass.getDeclaredField("cachePolicy");
positiveField.setAccessible(true);
Field negativeField =
cachePolicyClass.getDeclaredField("negativeCachePolicy");
negativeField.setAccessible(true);
int[] result = new int[2];
synchronized (cachePolicyClass) {
result[0] = positiveField.getInt(null);
result[1] = negativeField.getInt(null);
positiveField.setInt(null, positive);
negativeField.setInt(null, negative);
}
return result;
} catch (Exception e) {
throw new RuntimeException(
"Unexpected exception when setting DNS cache: " + e, e);
}
}
/**
* This implementation returns the loopback address for hosts that match a
* current alias.
*/
@Override
public InetAddress[] lookupAllHostAddr(String host)
throws UnknownHostException {
if (!aliases.contains(host)) {
logger.info("LocalAliasNameService.lookupAllHostAddr:" +
" Unknown host: " + host);
throw new UnknownHostException("Unknown host: " + host);
}
final InetAddress lh = getLocalHost();
logger.info("LocalAliasNameService.lookupAllHostAddr:" + host +
" => " + lh);
return new InetAddress[] { lh };
}
/**
* Compute the local host if needed, avoiding circularities. The main name
* service provider should provide the local host when called from
* InetAddress.getLocalHost, so it should be OK to throw
* UnknownHostException if this method is called recursively.
*/
private static synchronized InetAddress getLocalHost()
throws UnknownHostException {
if (localHost == null) {
if (computingLocalHost) {
throw new UnknownHostException("Local host");
}
computingLocalHost = true;
try {
localHost = InetAddress.getLocalHost();
} finally {
computingLocalHost = false;
}
}
return localHost;
}
/**
* This implementation returns one of the current aliases if the argument
* matches the loopback address and there is at least one alias.
*/
@Override
public String getHostByAddr(byte[] addr)
throws UnknownHostException {
final InetAddress inetAddr = InetAddress.getByAddress(addr);
if (!getLocalHost().equals(inetAddr.getHostAddress())) {
logger.info("LocalAliasNameService.getHostByAddr:" +
" No mapping for address");
throw new UnknownHostException("No mapping for address");
}
synchronized (aliases) {
final Iterator<String> iter = aliases.iterator();
if (iter.hasNext()) {
String hostname = iter.next();
logger.info("LocalAliasNameService.getHostByAddr: " +
hostname);
return hostname;
}
}
logger.info("LocalAliasNameService.getHostByAddr:" +
" No mapping for address");
throw new UnknownHostException("No mapping for address");
}
}