Coverage Report - org.apache.commons.id.uuid.state.StateHelper
 
Classes in this File Line Coverage Branch Coverage Complexity
StateHelper
81%
51/63
81%
13/16
2.714
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one or more
 3  
  * contributor license agreements.  See the NOTICE file distributed with
 4  
  * this work for additional information regarding copyright ownership.
 5  
  * The ASF licenses this file to You under the Apache License, Version 2.0
 6  
  * (the "License"); you may not use this file except in compliance with
 7  
  * the License.  You may obtain a copy of the License at
 8  
  *
 9  
  *      http://www.apache.org/licenses/LICENSE-2.0
 10  
  *
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 
 18  
 package org.apache.commons.id.uuid.state;
 19  
 
 20  
 import java.io.IOException;
 21  
 import java.net.InetAddress;
 22  
 import java.net.UnknownHostException;
 23  
 import java.util.Collection;
 24  
 import java.util.Iterator;
 25  
 import java.util.Random;
 26  
 import java.util.StringTokenizer;
 27  
 
 28  
 import org.apache.commons.id.DecoderException;
 29  
 import org.apache.commons.discovery.tools.DiscoverClass;
 30  
 import org.apache.commons.id.uuid.Bytes;
 31  
 import org.apache.commons.id.uuid.Constants;
 32  
 import org.apache.commons.id.uuid.clock.Clock;
 33  
 import org.apache.commons.id.uuid.clock.OverClockedException;
 34  
 import org.apache.commons.id.DigestUtils;
 35  
 import org.apache.commons.id.Hex;
 36  
 
 37  
 /**
 38  
  * <p><code>StateHelper</code> provides helper methods for the uuid state
 39  
  * implementations.</p>
 40  
  *
 41  
  * @author Commons-Id team
 42  
  * @version $Id: StateHelper.java 480488 2006-11-29 08:57:26Z bayard $
 43  
  */
 44  
 public final class StateHelper implements Constants {
 45  
     /** The key for the System.property containing the ClockImpl String. */
 46  3
     public static final String UUID_CLOCK_IMPL_PROPERTY_KEY = Clock.class.getName();
 47  
 
 48  
     /** The key for the System.property containing the StateImpl String. */
 49  1
     public static final String UUID_STATE_IMPL_PROPERTY_KEY = State.class.getName();
 50  
 
 51  
     /** Array length of node bytes. */
 52  
     public static final int NODE_ID_BYTE_LENGTH = 6;
 53  
 
 54  
     /** Number of bytes in a short. */
 55  
     public static final short BYTES_IN_SHORT = 2;
 56  
 
 57  
     /** Number of postitions to shift when shifting by one byte. */
 58  
     public static final short SHIFT_BY_BYTE = 8;
 59  
 
 60  
     /** Number of postitions to shift when shifting by one byte. */
 61  
     public static final short HOSTNAME_MAX_CHAR_LEN = 255;
 62  
 
 63  
     /** OR-Mask to set the node's multicast bit true. */
 64  
     private static final int MULTICAST_BIT_SET = 0x80;
 65  
 
 66  
     /** The maximum character length of a long */
 67  
     private static final short LONG_CHAR_LEN = 19;
 68  
 
 69  
     /** Standard one page buffer size */
 70  
     private static final int BUF_PAGE_SZ = 1024;
 71  
 
 72  
     /** Start of the XML document used to store state persistence in XML */
 73  
     protected static final String XML_DOC_START = "<?xml version=\"1.0\""
 74  
         + " encoding=\"UTF-8\" ?>"
 75  
         + "\n<!DOCTYPE uuidstate [\n"
 76  
         + "   <!ELEMENT uuidstate (node*)>\n"
 77  
         + "   <!ELEMENT node EMPTY>\n"
 78  
         + "   <!ATTLIST node id ID #REQUIRED>\n"
 79  
         + "   <!ATTLIST node clocksequence CDATA #IMPLIED>\n"
 80  
         + "   <!ATTLIST node lasttimestamp CDATA #IMPLIED>\n]>"
 81  
         + "\n<uuidstate synchInterval=\"";
 82  
 
 83  
     /** End of document start */
 84  
     protected static final String XML_DOC_START_END = "\">";
 85  
     /** Start of XML node tag */
 86  
     protected static final String XML_NODE_TAG_START = "\n\t<node id=\"";
 87  
     /** After id of XML node tag */
 88  
     protected static final String XML_NODE_TAG_AFTER_ID = "\" clocksequence=\"";
 89  
     /** After clock sequence of XML node tag */
 90  
     protected static final String XML_NODE_TAG_AFTER_CSEQ = "\" timestamp=\"";
 91  
     /** End of XML node tag */
 92  
     protected static final String XML_NODE_TAG_END = "\" />";
 93  
     /** End of the XML document used to store state persistence in XML */
 94  
     protected static final String XML_DOC_END = "\n</uuidstate>";
 95  
 
 96  
     /** Number of tokens in a MAC address with "-" as separator */
 97  
     private static final short MAC_ADDRESS_TOKEN_COUNT = 6;
 98  
 
 99  
     /** String length of a MAC address with "-" as separator */
 100  
     private static final short MAC_ADDRESS_CHAR_LENGTH = 17;
 101  
 
 102  
     /** Default constructor */
 103  
     private StateHelper() {
 104  0
         super();
 105  0
     }
 106  
 
 107  
     /**
 108  
      * <p>Creates a Random node identifier as described in IEFT UUID URN
 109  
      * specification.</p>
 110  
      *
 111  
      * @return a random node idenfifier based on MD5 of system information.
 112  
      */
 113  
     public static byte[] randomNodeIdentifier() {
 114  
         //Holds the 16 byte MD5 value
 115  4
         byte[] seed = new byte[UUID_BYTE_LENGTH];
 116  
         //Set the initial string buffer capacity
 117  
         //Time + Object.hashCode + HostName + Guess of all system properties
 118  4
         int bufSize = (LONG_CHAR_LEN * 2) + HOSTNAME_MAX_CHAR_LEN + (2 * BUF_PAGE_SZ);
 119  4
         StringBuffer randInfo = new StringBuffer(bufSize);
 120  
         //Add current time
 121  4
         long time = 0;
 122  
         try {
 123  4
             time = getClockImpl().getUUIDTime();
 124  0
         } catch (OverClockedException oce) {
 125  0
             time = System.currentTimeMillis();
 126  1
         }
 127  1
         randInfo.append(time);
 128  
 
 129  
         //Add hostname
 130  
         try {
 131  1
             InetAddress address = InetAddress.getLocalHost();
 132  1
             randInfo.append(address.getHostName());
 133  0
         } catch (UnknownHostException ukhe) {
 134  0
             randInfo.append("Host Unknown");
 135  1
         }
 136  
         //Add something else "random"
 137  1
         randInfo.append(new Object().hashCode());
 138  
 
 139  
         //Add system properties
 140  1
         Collection info = System.getProperties().values();
 141  1
         Iterator it = info.iterator();
 142  2
         while (it.hasNext()) {
 143  1
             randInfo.append(it.next());
 144  1
         }
 145  
         //MD5 Hash code the system information to get a node id.
 146  1
         seed = DigestUtils.md5(randInfo.toString());
 147  
 
 148  
         //Return upper 6 bytes of hash
 149  1
         byte[] raw = new byte[NODE_ID_BYTE_LENGTH];
 150  1
         System.arraycopy(seed, 0, raw, 0, NODE_ID_BYTE_LENGTH);
 151  
 
 152  
         //Per draft set multi-cast bit true
 153  1
         raw[0] |= MULTICAST_BIT_SET;
 154  
 
 155  1
         return raw;
 156  
     }
 157  
 
 158  
     /**
 159  
      * <p>Generates a new security quality random clock sequence.</p>
 160  
      *
 161  
      * @return a new security quality random clock sequence.
 162  
      */
 163  
     public static short newClockSequence() {
 164  11
         Random random = new Random();
 165  11
         byte[] bytes = new byte[BYTES_IN_SHORT];
 166  11
         random.nextBytes(bytes);
 167  11
         return (short) (Bytes.toShort(bytes) & 0x3FFF);
 168  
     }
 169  
 
 170  
     /**
 171  
      * <p>Returns the Clock implementation using commons discovery.</p>
 172  
      *
 173  
      * @return the Clock implementation using commons discovery.
 174  
      */
 175  
     public static Clock getClockImpl() {
 176  32
         Clock c = null;
 177  
         try {
 178  32
              DiscoverClass dc = new DiscoverClass();
 179  32
              c = (Clock) dc.newInstance(
 180  
              Clock.class,
 181  
              Clock.DEFAULT_CLOCK_IMPL);
 182  0
         } catch (Exception ex) {
 183  
              // ignore as default implementation will be used.
 184  32
         }
 185  32
         return c;
 186  
     }
 187  
 
 188  
     /**
 189  
      * <p>Returns the <code>State</code> implementation in use.</p>
 190  
      *
 191  
      * @return the <code>State</code> implementation in use.
 192  
      */
 193  
     public static State getStateImpl() {
 194  5
         State s = null;
 195  
         try {
 196  5
              DiscoverClass dc = new DiscoverClass();
 197  5
              s = (State) dc.newInstance(
 198  
              State.class,
 199  
              State.DEFAULT_STATE_IMPL);
 200  0
         } catch (Exception ex) {
 201  
              // ignore as default implementation will be used.
 202  5
         }
 203  5
         return s;
 204  
     }
 205  
 
 206  
     /**
 207  
      * <p>Utility method decodes a valid MAC address in the form of
 208  
      * XX-XX-XX-XX-XX-XX where each XX represents a hexidecimal value.</p>
 209  
      * 
 210  
      * <p> Returns null if the address can not be decoded. </p>
 211  
      *
 212  
      * @param address the String hexidecimal dash separated MAC address.
 213  
      * @return a byte array representing the the address. Null if not a valid address.
 214  
      */
 215  
     public static byte[] decodeMACAddress(String address) {
 216  25
         StringBuffer buf = new StringBuffer(MAC_ADDRESS_TOKEN_COUNT * 2);
 217  25
         StringTokenizer tokens = new StringTokenizer(address, "-");
 218  25
         if (tokens.countTokens() != MAC_ADDRESS_TOKEN_COUNT) {
 219  0
             return null;
 220  
         } else {
 221  175
             for (int i = 0; i < MAC_ADDRESS_TOKEN_COUNT; i++) {
 222  150
                 buf.append(tokens.nextToken());
 223  
             }
 224  
         }
 225  
         try {
 226  25
             char[] c = buf.toString().toCharArray();
 227  25
             return Hex.decodeHex(c);
 228  0
         } catch (DecoderException de) {
 229  0
             de.printStackTrace();
 230  0
             return null;
 231  
         }
 232  
     }
 233  
 
 234  
     /**
 235  
      * <p>Returns the node id / address byte array in it's hexidecimal string
 236  
      * representation in the following format: XX-XX-XX-XX-XX-XX where each XX
 237  
      * represents a hexidecimal value.</p>
 238  
      *
 239  
      * @param address the 6 byte node id / address.
 240  
      * @return the node id /address byte array in as hexidecimal with dash
 241  
      * separating each octet.
 242  
      * @throws IOException an Input Output Exception.
 243  
      */
 244  
     public static String encodeMACAddress(byte[] address) throws IOException {
 245  11
        char[] chars = Hex.encodeHex(address);
 246  11
        StringBuffer buf = new StringBuffer(MAC_ADDRESS_CHAR_LENGTH);
 247  143
        for (int i = 0; i < chars.length; i++) {
 248  132
                buf.append(chars[i]);
 249  132
                if (i != chars.length - 1 && i % 2 != 0) {
 250  55
                    buf.append("-");
 251  
                }
 252  
        }
 253  11
        return buf.toString().toUpperCase();
 254  
     }
 255  
 }