Coverage Report - com.allanbank.mongodb.util.ServerNameUtils
 
Classes in this File Line Coverage Branch Coverage Complexity
ServerNameUtils
85%
68/80
82%
23/28
7.5
 
 1  
 /*
 2  
  * #%L
 3  
  * ServerNameUtils.java - mongodb-async-driver - Allanbank Consulting, Inc.
 4  
  * %%
 5  
  * Copyright (C) 2011 - 2014 Allanbank Consulting, Inc.
 6  
  * %%
 7  
  * Licensed under the Apache License, Version 2.0 (the "License");
 8  
  * you may not use this file except in compliance with the License.
 9  
  * You may obtain a copy of the License at
 10  
  * 
 11  
  *      http://www.apache.org/licenses/LICENSE-2.0
 12  
  * 
 13  
  * Unless required by applicable law or agreed to in writing, software
 14  
  * distributed under the License is distributed on an "AS IS" BASIS,
 15  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 16  
  * See the License for the specific language governing permissions and
 17  
  * limitations under the License.
 18  
  * #L%
 19  
  */
 20  
 package com.allanbank.mongodb.util;
 21  
 
 22  
 import java.net.InetAddress;
 23  
 import java.net.InetSocketAddress;
 24  
 import java.net.UnknownHostException;
 25  
 import java.util.regex.Pattern;
 26  
 
 27  
 /**
 28  
  * ServerNameUtils provides the ability to generate a normalized name for a
 29  
  * server.
 30  
  * 
 31  
  * @api.no This class is <b>NOT</b> part of the drivers API. This class may be
 32  
  *         mutated in incompatible ways between any two releases of the driver.
 33  
  * @copyright 2012-2013, Allanbank Consulting, Inc., All Rights Reserved
 34  
  */
 35  
 public class ServerNameUtils {
 36  
     /** The default MongoDB port. */
 37  
     public static final int DEFAULT_PORT = 27017;
 38  
 
 39  
     /** The length of an IPv6 address in bytes. */
 40  
     public static final int IPV6_LENGTH = 16;
 41  
 
 42  
     /**
 43  
      * Pattern to match a literal IPv6 address with an elipse; e.g.,
 44  
      * fe80::250:56ff:fec0:1
 45  
      */
 46  
     private static final Pattern IPV6_ELLIPSED_PATTERN;
 47  
 
 48  
     /** Pattern to match a fully specified IPv6. */
 49  
     private static final Pattern IPV6_PATTERN;
 50  
 
 51  
     static {
 52  
         final String hexWord = "[0-9A-Fa-f]{1,4}";
 53  1
         IPV6_ELLIPSED_PATTERN = Pattern.compile("^((?:" + hexWord + "(?::"
 54  
                 + hexWord + ")*)?)::((?:" + hexWord + "(?::" + hexWord
 55  
                 + ")*)?)$");
 56  1
         IPV6_PATTERN = Pattern.compile("^(?:" + hexWord + ":){7}" + hexWord
 57  
                 + "$");
 58  1
     }
 59  
 
 60  
     /**
 61  
      * Creates a normalized form of the {@link InetSocketAddress} in the form
 62  
      * '[server]:[port]'.
 63  
      * 
 64  
      * @param address
 65  
      *            The address to generate a normalized form for.
 66  
      * @return The normalized address.
 67  
      */
 68  
     public static String normalize(final InetSocketAddress address) {
 69  1825
         final StringBuilder b = new StringBuilder();
 70  
 
 71  
         String name;
 72  1825
         if (address.isUnresolved()) {
 73  1234
             name = address.getHostName();
 74  
         }
 75  
         else {
 76  591
             name = address.getAddress().getHostName();
 77  
         }
 78  
 
 79  
         // Check for a raw IPv6 address and wrap in RFC 2732 format.
 80  1825
         if (IPV6_ELLIPSED_PATTERN.matcher(name).matches()
 81  
                 || IPV6_PATTERN.matcher(name).matches()) {
 82  10
             b.append('[');
 83  10
             b.append(name);
 84  10
             b.append(']');
 85  
         }
 86  
         else {
 87  1815
             b.append(name);
 88  
         }
 89  
 
 90  1825
         b.append(':');
 91  1825
         b.append(address.getPort());
 92  
 
 93  1825
         return b.toString();
 94  
     }
 95  
 
 96  
     /**
 97  
      * Normalizes the name into a '[server]:[port]' string. If a port component
 98  
      * is not provided then port 27017 is assumed.
 99  
      * 
 100  
      * @param server
 101  
      *            The server[:port] string.
 102  
      * @return The normailzed server string.
 103  
      */
 104  
     public static String normalize(final String server) {
 105  11
         final String name = server;
 106  11
         int port = DEFAULT_PORT;
 107  
 
 108  11
         final int firstBracket = server.indexOf('[');
 109  11
         final int lastBracket = server.lastIndexOf(']');
 110  11
         final int colonIndex = server.lastIndexOf(':');
 111  
 
 112  
         // Check for RFC 2732.
 113  11
         if ((firstBracket == 0) && (lastBracket > 0)) {
 114  2
             if (colonIndex == (lastBracket + 1)) {
 115  1
                 final String portString = server.substring(colonIndex + 1);
 116  
                 try {
 117  1
                     Integer.parseInt(portString);
 118  
 
 119  
                     // Its a good name no need to create another string.
 120  1
                     return server;
 121  
                 }
 122  0
                 catch (final NumberFormatException nfe) {
 123  
                     // Not a port after the colon. Move on.
 124  0
                     port = DEFAULT_PORT;
 125  
                 }
 126  
             }
 127  
 
 128  
             // No port. Add it.
 129  1
             final String namePart = server.substring(0, lastBracket + 1);
 130  1
             return namePart + ":" + port;
 131  
         }
 132  
 
 133  9
         if (0 <= colonIndex) {
 134  5
             final int previousColon = server.lastIndexOf(':', colonIndex - 1);
 135  5
             if (0 <= previousColon) {
 136  
                 // Colon in the host name. Might be an IPv6 address. Try to
 137  
                 // parse the whole thing as an address and if it works then
 138  
                 // assume no port.
 139  
                 try {
 140  2
                     final InetAddress addr = InetAddress.getByName(server);
 141  1
                     final byte[] bytes = addr.getAddress();
 142  
 
 143  
                     // Is it an IPv6 address?
 144  1
                     if (bytes.length == IPV6_LENGTH) {
 145  
                         // Yep - add the default port and wrap in RFC 2732
 146  1
                         return "[" + server + "]:" + DEFAULT_PORT;
 147  
                     }
 148  
                 }
 149  1
                 catch (final UnknownHostException uhe) {
 150  
                     // OK - fall through to being a port.
 151  1
                     final String addrString = server.substring(0, colonIndex);
 152  1
                     final String portString = server.substring(colonIndex + 1);
 153  
                     try {
 154  1
                         final InetAddress addr = InetAddress
 155  
                                 .getByName(addrString);
 156  1
                         final byte[] bytes = addr.getAddress();
 157  
 
 158  
                         // Is it an IPv6 address?
 159  1
                         if (bytes.length == IPV6_LENGTH) {
 160  1
                             port = Integer.parseInt(portString);
 161  
 
 162  1
                             return "[" + addrString + "]:" + port;
 163  
                         }
 164  
                     }
 165  0
                     catch (final NumberFormatException nfe) {
 166  
                         // Not a port after the colon. Move on.
 167  0
                         port = DEFAULT_PORT;
 168  
                     }
 169  0
                     catch (final UnknownHostException uhe2) {
 170  
                         // OK - fall through to being a port.
 171  0
                         uhe2.hashCode(); // Shhh - PMD.
 172  0
                     }
 173  0
                 }
 174  
             }
 175  
 
 176  3
             final String portString = server.substring(colonIndex + 1);
 177  
             try {
 178  3
                 Integer.parseInt(portString);
 179  
 
 180  
                 // Its a good name no need to create another string.
 181  2
                 return server;
 182  
             }
 183  1
             catch (final NumberFormatException nfe) {
 184  
                 // Not a port after the colon. Move on.
 185  1
                 port = DEFAULT_PORT;
 186  
             }
 187  
         }
 188  
 
 189  5
         return name + ':' + port;
 190  
     }
 191  
 
 192  
     /**
 193  
      * Parse the name into a {@link InetSocketAddress}. If a port component is
 194  
      * not provided then port 27017 is assumed.
 195  
      * 
 196  
      * @param server
 197  
      *            The server[:port] string.
 198  
      * @return The {@link InetSocketAddress} parsed from the server string.
 199  
      */
 200  
     public static InetSocketAddress parse(final String server) {
 201  262
         String name = server;
 202  262
         int port = DEFAULT_PORT;
 203  
 
 204  262
         final int firstBracket = server.indexOf('[');
 205  262
         final int lastBracket = server.lastIndexOf(']');
 206  262
         final int colonIndex = server.lastIndexOf(':');
 207  
         // Check for RFC 2732.
 208  262
         if ((firstBracket == 0) && (lastBracket > 0)) {
 209  2
             if ((lastBracket + 1) == colonIndex) {
 210  1
                 final String portString = server.substring(colonIndex + 1);
 211  
                 try {
 212  1
                     port = Integer.parseInt(portString);
 213  1
                     name = server.substring(0, colonIndex);
 214  
                 }
 215  0
                 catch (final NumberFormatException nfe) {
 216  
                     // Not a port after the colon. Move on.
 217  0
                     port = DEFAULT_PORT;
 218  1
                 }
 219  1
             }
 220  
         }
 221  260
         else if (colonIndex > 0) {
 222  210
             final String portString = server.substring(colonIndex + 1);
 223  
             try {
 224  210
                 port = Integer.parseInt(portString);
 225  206
                 name = server.substring(0, colonIndex);
 226  
             }
 227  4
             catch (final NumberFormatException nfe) {
 228  
                 // Not a port after the colon. Move on.
 229  4
                 port = DEFAULT_PORT;
 230  
 
 231  206
             }
 232  
         }
 233  
 
 234  262
         return new InetSocketAddress(name, port);
 235  
     }
 236  
 
 237  
     /**
 238  
      * Creates a new ServerNameUtils.
 239  
      */
 240  0
     private ServerNameUtils() {
 241  
         // nothing
 242  0
     }
 243  
 
 244  
 }