/*
 * The contents of this file are subject to the terms
 * of the Common Development and Distribution License
 * (the License).  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the license at
 * https://glassfish.dev.java.net/public/CDDLv1.0.html or
 * glassfish/bootstrap/legal/CDDLv1.0.txt.
 * See the License for the specific language governing
 * permissions and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL
 * Header Notice in each file and include the License file
 * at glassfish/bootstrap/legal/CDDLv1.0.txt.
 * If applicable, add the following below the CDDL Header,
 * with the fields enclosed by brackets [] replaced by
 * you own identifying information:
 * "Portions Copyrighted [year] [name of copyright owner]"
 *
 * Copyright (c) Ericsson AB, 2004-2007. All rights reserved.
 */
package com.ericsson.ssa.sip.dns;

import com.ericsson.eas.telurl.TelUrlResolver;
import com.ericsson.eas.telurl.TelUrlResolverException;

import com.ericsson.ssa.sip.SipFactoryImpl;
import com.ericsson.ssa.sip.SipURIImpl;

import org.xbill.DNS.AAAARecord;
import org.xbill.DNS.ARecord;
import org.xbill.DNS.Cache;
import org.xbill.DNS.DClass;
import org.xbill.DNS.Lookup;
import org.xbill.DNS.NAPTRRecord;
import org.xbill.DNS.Name;
import org.xbill.DNS.Record;
import org.xbill.DNS.SRVRecord;
import org.xbill.DNS.TextParseException;
import org.xbill.DNS.Type;

import java.io.IOException;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
import java.util.logging.Level;

// inserted by hockey (automatic)
import java.util.logging.Logger;

import javax.servlet.sip.ServletParseException;
import javax.servlet.sip.SipURI;
import javax.servlet.sip.TelURL;
import javax.servlet.sip.URI;


/**
 * DNS resolver.
 *
 * @reviewed qmigkra 2006-oct-12
 * @reviewed ejoelbi 2006-nov-07
 */

/**
 * @reviewed qmaghes 2007-mars-21
 */
public class DnsResolver implements TelUrlResolver {
    protected static DnsResolver myInstance = null;
    private Random myRandomInstance = new java.util.Random();
    private TargetFailedRegister myFailedTargets = new TargetFailedRegister();
    private Logger logger = (Logger) Logger.getLogger("SipContainer");
    private String myEnumTopDomain = "e164.arpa"; // this can be overidden by

    // preferences or JMX
    public DnsResolver() {
    }

    public static synchronized DnsResolver getInstance() {
        if (myInstance == null) {
            myInstance = new DnsResolver();
        }

        return myInstance;
    }

    public void setTargetFailed(TargetTuple target, int expireValue) {
        myFailedTargets.registerFailed(target, expireValue);
    }

    public boolean isTargetFailed(TargetTuple target) {
        return myFailedTargets.isFailed(target);
    }

    public TargetTuple lookupNameFromSRV(SRVRecord record,
        SipTransports theProtocol)
        throws DnsLookupFailedException, SrvPrioLevelNoValidIpException {
        // orginal
        // TargetTuple.createFromSrv(srv, theProtocol);
        // lookup corresponding A-records
        String srvTarget = record.getTarget().toString(); // String mySRVDomain =
                                                          // record.getTarget().toString();
                                                          // check if srv target is numeric instead of an domain

        if (TargetResolver.isNumericIp(srvTarget)) {
            // the srv record target is an ipadress which has failed
            // lets return a null target (which means that default values might be
            // used
            // for "Selecting SIP Servers".
            // TODO check if an exception should be thrown to the
            // transport layer
            TargetTuple tuple = new TargetTuple(theProtocol, srvTarget,
                    record.getPort());

            if (myFailedTargets.isFailed(tuple)) {
                return null;
            }

            return tuple;
        }

        String[] ips = lookupNameRecords(srvTarget);

        // first take care of the most frequent (?) case with only 1 A record
        // assigned to the SRV record
        if ((ips.length == 1)) {
            TargetTuple tuple = new TargetTuple(theProtocol, ips[0],
                    record.getPort());

            if (myFailedTargets.isFailed(tuple)) {
                return null;
            }

            return tuple;
        }

        /*
         * add randomization to distribute among A-records (load balance) however
         * to avoid a possibly INFINITE loop make maximum ips.length attempts to
         * find a suitable target which has not failed otherwise test records
         * sequentially
         */
        int randomIndexToUse = 0;
        int attempts = 0;

        while ((attempts < (ips.length))) {
            attempts++;
            // use a random index (which is not round-robin but which distributes
            // sufficiently)
            randomIndexToUse = getRandomIndex(ips.length);

            String ip = ips[randomIndexToUse];
            TargetTuple tuple = new TargetTuple(theProtocol, ip,
                    record.getPort());

            if (myFailedTargets.isFailed(tuple) == false) {
                return tuple;
            }
        }

        // did not find a record by random which hasent failed...
        // Lets test records sequentially
        for (int i = 0; i < ips.length; i++) {
            TargetTuple tuple = new TargetTuple(theProtocol, ips[i],
                    record.getPort());

            if (myFailedTargets.isFailed(tuple) == false) {
                return tuple;
            }
        }

        throw new SrvPrioLevelNoValidIpException(
            "found no valid ip adress for this srv record:" + srvTarget);
    }

    public Record[] doLookup(String domainNameToLookup, int type)
        throws DnsLookupFailedException {
        if (logger.isLoggable(Level.FINE)) {
            logger.log(Level.FINE,
                "doLookup: " + domainNameToLookup + " " + Type.string(type));
            logger.log(Level.FINE,
                "current #cacheentries: " +
                Lookup.getDefaultCache(DClass.IN).getSize());
        }

        // domain need to be fully qualified with new dnsjava version
        if (domainNameToLookup.charAt(domainNameToLookup.length() - 1) != '.') {
            domainNameToLookup += ".";
        }

        Lookup lkup = null;

        try {
            lkup = new Lookup(domainNameToLookup, type);
        } catch (TextParseException e) {
            // we wants only one "checked" exception, lets wrap into a
            // DnsLookupFailedException
            throw new DnsLookupFailedException("Failed to lookup domain:" +
                domainNameToLookup + " error:" + e.getMessage());
        }

        Record[] records = lkup.run();
        int res = lkup.getResult();

        if (res == Lookup.TRY_AGAIN) {
            throw new DnsServerUnavailableException(lkup.getErrorString());
        } else if (lkup.getResult() != Lookup.SUCCESSFUL) {
            throw new DnsLookupFailedException("no such domain:" +
                domainNameToLookup + " error:" + lkup.getErrorString());
        }

        return records;
    }

    public TargetTuple lookupSRVRecord(SipTransports theProtocol,
        String domainNameToLookup) {
        try {
            Record[] records = doLookup(domainNameToLookup, Type.SRV);

            if (logger.isLoggable(Level.FINE)) {
                logger.log(Level.FINE,
                    "number of DNS/SRV records:" + records.length);
            }

            // 1) sort on prio, weight
            java.util.Arrays.sort(records, new SrvComparator());

            // 2) calculate runningSum
            int[] runningSum = new int[records.length];
            int deltaSum = 0;
            int numPriorityGroups = 0;
            int[] priorityGroupBoundaries = new int[records.length];
            int lastPrio = -1;
            int prioIndex = 0;

            for (int i = 0; i < records.length; i++) {
                SRVRecord srv = (SRVRecord) records[i];

                if (srv.getPriority() != lastPrio) {
                    lastPrio = srv.getPriority();
                    deltaSum = 0;
                    priorityGroupBoundaries[prioIndex] = i;
                    prioIndex++;
                    numPriorityGroups++;
                }

                deltaSum += srv.getWeight();
                runningSum[i] = deltaSum;

                //
                if (logger.isLoggable(Level.FINE)) {
                    logger.log(Level.FINE,
                        "SRVRecord.toString():" + srv.toString());
                }

                /*
                 * logger.log(Level.FINE, "SRVRecord.toString():" + srv.toString());
                 * //logger.log(Level.FINE, "Host " + srv.getTarget());
                 * logger.log(Level.FINE, "Priority:"+ srv.getPriority()); logger.log(Level.FINE, "
                 * Weight:"+ srv.getWeight()); logger.log(Level.FINE, " runningSum:"+
                 * runningSum[i]);
                 */
            }

            for (prioIndex = 0; prioIndex < numPriorityGroups; prioIndex++) {
                if (logger.isLoggable(Level.FINE)) {
                    logger.log(Level.FINE, "SRV priolevel:" + prioIndex);
                }

                try {
                    return lookupSRVPrioLevel(prioIndex, theProtocol,
                        runningSum, records, numPriorityGroups,
                        priorityGroupBoundaries);
                } catch (SrvPrioLevelNoValidIpException noValidIPExc) {
                    continue;
                }
            }
        }
        //
        catch (DnsLookupFailedException dLookupEx) {
            if (logger.isLoggable(Level.FINE)) {
                logger.log(Level.FINE, dLookupEx.getMessage());
            }
        }

        if (logger.isLoggable(Level.FINE)) {
            logger.log(Level.FINE,
                "DNS/SRV No record found: " + domainNameToLookup);
        }

        return null;
    }

    public TargetTuple lookupSRVPrioLevel(int prioIndex,
        SipTransports theProtocol, int[] runningSum, Record[] records,
        int numPriorityGroups, int[] priorityGroupBoundaries)
        throws DnsLookupFailedException, SrvPrioLevelNoValidIpException {
        int firstIndex = priorityGroupBoundaries[prioIndex];
        int lastIndex = ((prioIndex + 1) < numPriorityGroups)
            ? (priorityGroupBoundaries[prioIndex + 1] - 1) : (records.length -
            1);
        int maxValue = runningSum[lastIndex] + 1;
        int testValue = getRandomIndex(maxValue);

        for (int i = firstIndex; i < (lastIndex + 1); i++) {
            SRVRecord srv = (SRVRecord) records[i];

            // logger.log(Level.FINE, "SRVRecord.toString():" + srv.toString());
            if (runningSum[i] >= testValue) {
                // this is it
                if (logger.isLoggable(Level.FINE)) {
                    logger.log(Level.FINE, "found srv:" + srv.toString());
                }

                return lookupNameFromSRV(srv, theProtocol);
            }
        }

        return null;
    }

    //
    public TargetTuple lookupSRVDirect(SipTransports protocol,
        String domainNameToLookup) throws DnsLookupFailedException {
        return lookupSRVDirect(protocol, domainNameToLookup, false);
    }

    public TargetTuple lookupSRVDirect(SipTransports protocol,
        String domainNameToLookup, boolean isUdpMTUExceeded)
        throws DnsLookupFailedException {
        if (isUdpMTUExceeded && (protocol == SipTransports.UDP_PROT)) {
            protocol = SipTransports.TCP_PROT;
        }

        try {
            switch (protocol.ordinal()) {
            case SipTransports.TCP:
            case SipTransports.UDP:
                return lookupSRVRecord(protocol,
                    getSrvDirectLookupId(protocol, domainNameToLookup));

            case SipTransports.UNDEFINED:default: {
                TargetTuple target = null;

                // first try with UDP then TCP if protocol is undefined.
                // we MAY try the protocols we support in any order RFC3263
                // Section
                // 4.1
                if ((isUdpMTUExceeded == false) &&
                        ((target = lookupSRVDirect(SipTransports.UDP_PROT,
                                domainNameToLookup)) != null)) {
                } else if ((target = lookupSRVDirect(SipTransports.TCP_PROT,
                                domainNameToLookup)) != null) {
                }

                return target;
            }
            }
        } catch (DnsLookupFailedException dnfe) {
            return null;
        }
    }

    private String getSrvDirectLookupId(SipTransports protocol,
        String domainNameToLookup) throws DnsLookupFailedException {
        String lookupStr = protocol.getSRVToken() + "." + domainNameToLookup;

        if (logger.isLoggable(Level.FINE)) {
            logger.log(Level.FINE, "getDirectSrvLookupString:" + lookupStr);
        }

        return lookupStr;
    }

    public void clearLocalCache() {
        Cache cache = Lookup.getDefaultCache(DClass.IN);
        cache.clearCache();
    }

    public String lookupName(String domainNameToLookup)
        throws Exception {
        String ip;

        if ((ip = lookupARecord(domainNameToLookup)) != null) {
            return ip;
        } else if ((ip = lookupAAAARecord(domainNameToLookup)) != null) {
            return ip;
        } else {
            return null;
        }
    }

    public String[] lookupNameRecords(String domainNameToLookup)
        throws DnsLookupFailedException {
        String[] ips = null;

        if ((ips = lookupARecords(domainNameToLookup)) != null) {
            return ips;
        } else if ((ips = lookupAAAARecords(domainNameToLookup)) != null) {
            return ips;
        } else {
            return null;
        }
    }

    public String lookupARecord(String domainNameToLookup)
        throws DnsLookupFailedException {
        String[] ips = lookupARecords(domainNameToLookup);

        if (ips == null) {
            return null;
        }

        int indexToUse = 0;

        if ((ips.length > 1)) {
            // we need only to distribute lookups when we have at least 2 A-records
            //
            // use a random index (which is not round-robin but which distributes
            // sufficiently)
            indexToUse = getRandomIndex(ips.length);
        }

        return ips[indexToUse];
    }

    public String[] lookupAAAARecords(String domainNameToLookup)
        throws DnsLookupFailedException {
        try {
            Record[] records = doLookup(domainNameToLookup, Type.AAAA);
            String[] ips = new String[records.length];

            for (int i = 0; i < records.length; i++)
                ips[i] = ((AAAARecord) records[i]).getAddress().toString();

            return ips;
        } catch (DnsLookupFailedException dLookupEx) {
            if (logger.isLoggable(Level.FINE)) {
                logger.log(Level.FINE, dLookupEx.getMessage());
            }
        }

        if (logger.isLoggable(Level.FINE)) {
            logger.log(Level.FINE,
                "DNS/A No record found: " + domainNameToLookup);
        }

        return null;
    }

    public String[] lookupARecords(String domainNameToLookup)
        throws DnsLookupFailedException {
        try {
            Record[] records = doLookup(domainNameToLookup, Type.A);
            String[] ips = new String[records.length];

            for (int i = 0; i < records.length; i++) //
             {
                ips[i] = ((ARecord) records[i]).getAddress().getHostAddress();
            }

            return ips;
        } catch (DnsLookupFailedException dLookupEx) {
            if (logger.isLoggable(Level.FINE)) {
                logger.log(Level.FINE, dLookupEx.getMessage());
            }
        }

        if (logger.isLoggable(Level.FINE)) {
            logger.log(Level.FINE,
                "DNS/A No record found: " + domainNameToLookup);
        }

        return null;
    }

    public String lookupAAAARecord(String domainNameToLookup)
        throws DnsLookupFailedException {
        try {
            Record[] records = doLookup(domainNameToLookup, Type.AAAA);
            int indexToUse = 0;

            if ((records.length > 1)) {
                // we need only to distribute lookups when we have at least 2
                // A-records
                //
                // use a random index (which is not round-robin but which
                // distributes sufficiently)
                indexToUse = getRandomIndex(records.length);
            }

            return ((AAAARecord) records[indexToUse]).getAddress().toString();
        } catch (DnsLookupFailedException dLookupEx) {
            if (logger.isLoggable(Level.FINE)) {
                logger.log(Level.FINE, dLookupEx.getMessage());
            }
        }

        if (logger.isLoggable(Level.FINE)) {
            logger.log(Level.FINE,
                "DNS/AAAA No record found: " + domainNameToLookup);
        }

        return null;
    }

    private int getRandomIndex(int upperIndex) {
        int indexToUse = myRandomInstance.nextInt(upperIndex);

        if (logger.isLoggable(Level.FINE)) {
            logger.log(Level.FINE,
                "Picked a random value between 0 and:" + upperIndex +
                " (excl). Random:" + indexToUse);
        }

        return indexToUse;
    }

    public TargetTuple lookupNAPTRRecord(String domainNameToLookup,
        boolean isUdpMTUExceeded) {
        try {
            Record[] records = doLookup(domainNameToLookup, Type.NAPTR);

            if (logger.isLoggable(Level.FINE)) {
                logger.log(Level.FINE,
                    "number of DNS/NAPTR records:" + records.length);
            }

            java.util.Arrays.sort(records, new NaptrComparator());

            // TODO NAPTR: Use the first record we support.
            // from where do we know which protocol this server supports/prefers?
            // NAPTR lookup on this host?
            // (but only within the implemented set (UDP, TCP)
            // Could we assume allways having NAPTR records for own host or
            // should we have an fallback in a "local" config file
            //
            // should we consider other protocol for loadbalancing/fallback
            // --> probably NO
            // I.e. when the protocol to use is decided via NAPTR then we stick
            // with that protocol
            // only exception is MTU size > 1300 bytes etc.
            //
            for (int i = 0; i < records.length; i++) {
                NAPTRRecord record = (NAPTRRecord) records[i];

                if (logger.isLoggable(Level.FINE)) {
                    logger.log(Level.FINE,
                        "NAPTRRecord.toString():" + record.toString());
                }

                String service = record.getService();

                // Lets check the NAPTR record. Do we support the protocol?
                // if MTUExceeded is true UDP should not be considered
                if (SipTransports.UDP_PROT.isSupported() &&
                        service.equals(SipTransports.UDP_PROT.getNAPTRId()) &&
                        (isUdpMTUExceeded == false)) {
                    return lookupSRVRecord(SipTransports.UDP_PROT,
                        record.getReplacement().toString());
                } else if (SipTransports.TCP_PROT.isSupported() &&
                        service.equals(SipTransports.TCP_PROT.getNAPTRId())) {
                    return lookupSRVRecord(SipTransports.TCP_PROT,
                        record.getReplacement().toString());
                }
            }
        } catch (DnsLookupFailedException dLookupEx) {
            if (logger.isLoggable(Level.FINE)) {
                logger.log(Level.FINE, dLookupEx.getMessage());
            }
        }

        if (logger.isLoggable(Level.FINE)) {
            logger.log(Level.FINE,
                "DNS/NAPTR No record found: " + domainNameToLookup);
        }

        return null;
    }

    public TelURL convertToTelURL(SipURI sipuri) throws TelUrlResolverException {
        String userParam = sipuri.getParameter("user");

        if ((userParam == null) || (userParam.equals("phone") == false)) {
            throw new TelUrlResolverException("Missing parameter user=phone");
        }

        StringBuilder userTelUrl = new StringBuilder();
        userTelUrl.append(SipFactoryImpl.TEL_URI_PROTOCOL);
        userTelUrl.append(':');
        userTelUrl.append(sipuri.getUser());

        try {
            return (TelURL) SipFactoryImpl.getInstance()
                                          .createURI(userTelUrl.toString());
        } catch (ServletParseException e) {
            throw new TelUrlResolverException(e);
        }
    }

    /*
     * This is the method which implements the interface TelUrlResolver This
     * class maps DnsServerUnavailableException to a IOException
     */
    public SipURI lookupSipURI(URI uri)
        throws TelUrlResolverException, IOException {
        try {
            return doLookupSipURI(uri);
        } catch (DnsServerUnavailableException e) {
            throw new IOException(e.getMessage());
        }
    }

    public boolean isTelephoneNumber(URI uri) {
        if (uri.isSipURI()) {
            String userParam = ((SipURI) uri).getParameter("user");

            if ((userParam == null) || (userParam.equals("phone") == false)) {
                return false;
            }

            return true;
        }

        return (uri.getScheme().equals(SipFactoryImpl.TEL_URI_PROTOCOL));
    }

    protected SipURI doLookupSipURI(URI uri) throws TelUrlResolverException {
        // validate uri
        if (!isTelephoneNumber(uri)) {
            throw new TelUrlResolverException("The uri is not a phone number:" +
                uri);
        }

        TelURL telUrl = null;

        if (uri.isSipURI()) {
            telUrl = convertToTelURL((SipURI) uri);
        } else if (uri.getScheme().equals(SipFactoryImpl.TEL_URI_PROTOCOL)) {
            telUrl = (TelURL) uri;
        }

        if (telUrl.isGlobal() == false) {
            throw new TelUrlResolverException(
                "The number is not a global number");
        }

        try {
            String AUS = EnumUtil.getAUSValue(telUrl.getPhoneNumber());
            String domainNameToLookup = EnumUtil.toDomain(AUS, myEnumTopDomain);

            return lookupTelUrlNAPTRRecord(AUS, domainNameToLookup,
                new HashSet<String>(), 1);
        } catch (DnsServerUnavailableException dnsue) {
            // All runtime exceptions except DnsServerUnavailableException
            // should be wrapped into a TelUrlResolverException
            throw dnsue;
        } catch (RuntimeException rte) {
            throw new TelUrlResolverException(rte);
        }
    }

    /**
     * @param AUS
     * @param e164arpa_domain
     * @param visited
     * @param depth
     * @return
     */
    protected SipURI lookupTelUrlNAPTRRecord(String AUS,
        String e164arpa_domain, Set<String> visited, int depth)
        throws TelUrlResolverException {
        if ((depth > EnumUtil.MAX_DEPTH) || !visited.add(e164arpa_domain)) {
            throw new TelUrlResolverException("Loop detected when resolving:" +
                AUS);
        }

        if (logger.isLoggable(Level.FINE)) {
            logger.log(Level.FINE,
                "depth:" + depth + " AUS:" + AUS + " domain:" +
                e164arpa_domain);
        }

        try {
            Record[] records = doLookup(e164arpa_domain, Type.NAPTR);
            Arrays.sort(records, new NaptrComparator());

            for (Record record : records) {
                NAPTRRecord naptrRecord = (NAPTRRecord) record;

                if (logger.isLoggable(Level.FINE)) {
                    logger.log(Level.FINE,
                        "number of DNS/NAPTR records:" + records.length);
                    logger.log(Level.FINE,
                        "NAPTRRecord.toString():" + record.toString());
                }

                String flag = naptrRecord.getFlags();
                String service = naptrRecord.getService();
                Name replacement = naptrRecord.getReplacement();

                if (service.equalsIgnoreCase("E2U+sip") ||
                        service.equalsIgnoreCase("sip+E2U")) {
                    if (logger.isLoggable(Level.FINE)) {
                        logger.log(Level.FINE,
                            "flag:" + naptrRecord.getFlags() + " flaglength:" +
                            naptrRecord.getFlags().length());
                        logger.log(Level.FINE,
                            "regexp:" + naptrRecord.getRegexp() +
                            " replacement:" + replacement);
                    }

                    if (!EnumUtil.isEmpty(replacement) && (flag.length() == 0)) {
                        return lookupTelUrlNAPTRRecord(AUS,
                            replacement.toString(), visited, ++depth);
                    }

                    String substitution = EnumUtil.applyRegExp(AUS,
                            naptrRecord.getRegexp());

                    if (substitution == null) {
                        // try next record
                        continue;
                    }

                    if (flag.equals("u")) {
                        int separator = substitution.indexOf(':');

                        if (separator == -1) {
                            throw new TelUrlResolverException(
                                "The regexp substitution result is not any supported uri:" +
                                substitution);
                        }

                        String schemePart = substitution.substring(0, separator);

                        String theUriPart = substitution.substring(separator +
                                1);

                        if (schemePart.equals(SipFactoryImpl.TEL_URI_PROTOCOL)) {
                            return lookupTelUrlNAPTRRecord(AUS,
                                EnumUtil.toDomain(theUriPart, myEnumTopDomain),
                                visited, ++depth);
                        } else if (schemePart.equals(
                                    SipFactoryImpl.SIP_URI_PROTOCOL) ||
                                schemePart.equals(
                                    SipFactoryImpl.SIPS_URI_PROTOCOL)) {
                            try {
                                SipURIImpl sipuri = new SipURIImpl(schemePart,
                                        theUriPart.getBytes(), 0);

                                // check user=phone etc here
                                String userParam = sipuri.getParameter("user");

                                if ((userParam != null) &&
                                        userParam.equals("phone")) {
                                    String userTelUrl = sipuri.getUser();

                                    return lookupTelUrlNAPTRRecord(AUS,
                                        EnumUtil.toDomain(userTelUrl,
                                            myEnumTopDomain), visited, ++depth);
                                }

                                return sipuri;
                            } catch (ServletParseException e) {
                                throw new TelUrlResolverException(e);
                            }
                        } else {
                            throw new TelUrlResolverException(
                                "The regexp substitution result is not any supported uri:" +
                                substitution);
                        }
                    } else if (flag.length() == 0) {
                        return lookupTelUrlNAPTRRecord(AUS, substitution,
                            visited, ++depth);
                    }

                    /* else ignore record */
                }

                /* else ignore record */
            }
        } catch (DnsLookupFailedException dLookupEx) {
            if (logger.isLoggable(Level.FINE)) {
                logger.log(Level.FINE, dLookupEx.getMessage());
            }
        }

        return null;
    }

    public void setEnumTopDomain(String topDomain) {
        myEnumTopDomain = topDomain;
    }

    public String getEnumTopDomain() {
        return myEnumTopDomain;
    }
}
