Coverage Report - org.apache.commons.id.uuid.state.ReadOnlyResourceStateImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
ReadOnlyResourceStateImpl
83%
24/29
50%
3/6
3.5
ReadOnlyResourceStateImpl$StateConfigHandler
85%
45/53
71%
22/31
3.5
 
 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.io.InputStream;
 22  
 import java.util.HashSet;
 23  
 import java.util.Set;
 24  
 
 25  
 import javax.xml.parsers.SAXParser;
 26  
 import javax.xml.parsers.SAXParserFactory;
 27  
 
 28  
 import org.xml.sax.Attributes;
 29  
 import org.xml.sax.SAXException;
 30  
 import org.xml.sax.helpers.DefaultHandler;
 31  
 
 32  
 /**
 33  
  * <p>The <code>ReadOnlyResourceStateImpl</code> is an implementation of the
 34  
  * <code>State</code> interface. This implementation provides better guarantees
 35  
  * that no duplicate UUID's will be generated; however since the only stateful
 36  
  * information provided is the IEEE 802 address the generator should use a
 37  
  * better choice is to use an implementation that also writes to persistent
 38  
  * storage each time the state is loaded a new clock sequence is used. If the
 39  
  * system time is adjusted backwards there is a possibility that a UUID generated
 40  
  * with the same clock sequence and time could be generated.
 41  
  *
 42  
  * @author Commons-Id team
 43  
  * @version $Id: ReadOnlyResourceStateImpl.java 480488 2006-11-29 08:57:26Z bayard $
 44  
  */
 45  
 public class ReadOnlyResourceStateImpl implements State {
 46  
 
 47  
     /** How often to write to stable storage - since this is read-only make it largest. */
 48  1
     static long synchronizeInterval = Long.MAX_VALUE;
 49  
 
 50  
     /** Collection of nodes to load or store. */
 51  1
     static HashSet nodes = new HashSet();
 52  
 
 53  
     /**
 54  
      * The key to use in locating the uuid configuration xml file from System
 55  
      * properties.
 56  
      */
 57  
     public static final String CONFIG_FILENAME_KEY = "org.apache.commons.id.uuid.config.resource.filename";
 58  
 
 59  
     /**
 60  
      * <p>Constructs a ReadOnlyResouceStateImpl.</p>
 61  
      */
 62  
     public ReadOnlyResourceStateImpl() {
 63  10
         super();
 64  10
     }
 65  
 
 66  
     /**
 67  
      * <p>Loads the System.property &quot;commons.uuid.configFileName&quot;
 68  
      * (default is &quot;uuid.conf&quot;) using commons.discovery.</p>
 69  
      * <p>
 70  
      * The uuid-[n].conf file is an xml file with the following syntax:<br>
 71  
      * <pre>
 72  
      * <?xml version="1.0" encoding="UTF-8" ?>
 73  
      * <!DOCTYPE uuidstate [
 74  
      * <!ELEMENT uuidstate (node*)>
 75  
      * <!ELEMENT node EMPTY>
 76  
      * <!ATTLIST node id ID #REQUIRED>
 77  
      * <!ATTLIST node clocksequence CDATA #IMPLIED>
 78  
      * <!ATTLIST node lasttimestamp CDATA #IMPLIED>
 79  
      * ]>
 80  
      * <uuidstate>
 81  
      * <node id="XX-XX-XX-XX-XX-XX" />
 82  
      * <node id="YY-YY-YY-YY-YY-YY" />
 83  
      * </uuidstate>
 84  
      * </pre>
 85  
      * </p><p>See the documentation for further information on configuration
 86  
      * tasks.</p>
 87  
      *
 88  
      * @throws IllegalStateException if the &quot;commons.uuid.configFileName&quot;
 89  
      * system property is not set or the resource cannot be loaded.
 90  
      * @throws SAXException if an xml parsing error occurs
 91  
      * @throws ParserConfigurationException if the parser cannot be loaded
 92  
      * @throws IOException if an error occurs reading the file
 93  
      * 
 94  
      * @see org.apache.commons.id.uuid.state.State#load()
 95  
      */
 96  
     public void load() throws Exception {
 97  
         // Get the resource name
 98  6
         String resourceName = System.getProperty(CONFIG_FILENAME_KEY);
 99  6
         if (resourceName == null) {
 100  0
             throw new IllegalStateException("No value set for system property: " 
 101  
                     + CONFIG_FILENAME_KEY);
 102  
         }
 103  
 
 104  
         // Load the resource
 105  6
         InputStream in = null;
 106  
         try {
 107  6
             in = ClassLoader.getSystemResourceAsStream(resourceName);
 108  6
             if (in == null) {
 109  0
                 throw new IllegalStateException(resourceName + 
 110  
                         " loaded as system resource is null");
 111  
             }
 112  
             //Do the XML parsing
 113  6
             parse(in);
 114  
         } finally {
 115  6
             if (in != null) {
 116  
                 try {
 117  6
                     in.close();
 118  0
                 } catch (IOException ioe) {
 119  
                     //Nothing to do at this point.
 120  6
                 }
 121  0
             }
 122  0
         }
 123  6
     }
 124  
 
 125  
     /**
 126  
      * @see State#getSynchInterval
 127  
      */
 128  
     public long getSynchInterval() {
 129  
         //Return Long.MAX_VALUE since this is readonly.
 130  10
         return Long.MAX_VALUE;
 131  
     }
 132  
 
 133  
     /**
 134  
      * @see org.apache.commons.id.uuid.state.State#getNodes()
 135  
      */
 136  
     public Set getNodes() {
 137  7
         return nodes;
 138  
     }
 139  
 
 140  
     /**
 141  
      * @see org.apache.commons.id.uuid.state.State#store(java.util.Set)
 142  
      */
 143  
     public void store(Set nodeSet) throws IOException {
 144  
         // Nothing to do - this is a ReadOnly implementation.
 145  2
         return;
 146  
     }
 147  
 
 148  
     /**
 149  
      * @see org.apache.commons.id.uuid.state.State#store(java.util.Set, long)
 150  
      */
 151  
     public void store(Set nodeSet, long timestamp) {
 152  
         // Nothing to do - this is a ReadOnly implementation.
 153  1
         return;
 154  
     }
 155  
 
 156  
     /**
 157  
      * <p>Parses the XML configuration into the <code>Node</code>s and places
 158  
      * into this instances node collection.</p>
 159  
      *
 160  
      * @param in the XML input stream to parse.
 161  
      */
 162  
     protected void parse(InputStream in) throws Exception {
 163  6
         DefaultHandler handler = new StateConfigHandler();
 164  6
         SAXParserFactory saxFactory = SAXParserFactory.newInstance();
 165  6
         saxFactory.setValidating(true);
 166  6
         SAXParser parser = saxFactory.newSAXParser();
 167  6
         parser.parse(in, handler);
 168  6
     }
 169  
    
 170  
     //--------------------------------------------------------------------------
 171  
     /**
 172  
      * Inner class to handle document processing of the configuration file.
 173  
      */
 174  6
     class StateConfigHandler extends DefaultHandler {
 175  
         /** Constant for the uuidstate tag */
 176  
         static final short UUID_STATE_TAG = 1;
 177  
         /** Constant string value for uuidstate tag */
 178  
         static final String UUID_STATE_TAG_STR = "uuidstate";
 179  
         /** Constant string value for the synchinterval attribute */
 180  
         static final String SYNCH_INTERVAL_STR = "synchinterval";
 181  
         /** Constant for the node tag */
 182  
         static final short NODE_TAG = 2;
 183  
         /** Constant string value for the node tag */
 184  
         static final String NODE_TAG_STR = "node";
 185  
         /** Constant string value for the id attribute */
 186  
         static final String ATTR_ID_STR = "id";
 187  
         /** Constant string value for the last clock sequence attribute */
 188  
         static final String ATTR_CLOCKSEQ_STR = "clocksequence";
 189  
         /** Constant string value for the last time stamp attribute */
 190  
         static final String ATTR_LASTIMESTAMP_STR = "timestamp";
 191  
 
 192  
         /**
 193  
          * Handle start of tag.
 194  
          * @see org.xml.sax.helpers.DefaultHandler#startElement(String, String, String, Attributes)
 195  
          */
 196  
         public void startElement(
 197  
             String namespaceURI,
 198  
             String simpleName,
 199  
             String qualifiedName,
 200  
             Attributes attributes)
 201  
             throws SAXException {
 202  
 
 203  18
             short currentTag = 0;
 204  
 
 205  18
             String element = simpleName;
 206  18
             if ("".equals(simpleName)) {
 207  18
                 element = qualifiedName;
 208  
             }
 209  18
             if (element.equalsIgnoreCase(UUID_STATE_TAG_STR)) {
 210  6
                 currentTag = UUID_STATE_TAG;
 211  6
             } else if (element.equalsIgnoreCase(NODE_TAG_STR)) {
 212  12
                 currentTag = NODE_TAG;
 213  
             }
 214  
             //Process attributes
 215  18
             if (attributes != null) {
 216  18
                 switch (currentTag) {
 217  
                     case 1 :
 218  6
                         processBodyTag(attributes);
 219  6
                         break;
 220  
                     case 2 :
 221  12
                         processNodeTag(attributes);
 222  12
                         break;
 223  
                     default :
 224  
                         break;
 225  
                 }
 226  
             }
 227  18
         }
 228  
 
 229  
         /**
 230  
          * <p>Processes the main body tag of document.</p>
 231  
          *
 232  
          * @param attributes - sax Attributes to process.
 233  
          */
 234  
         private void processBodyTag(Attributes attributes) {
 235  12
             for (int i = 0; i < attributes.getLength(); i++) {
 236  6
                 String attributeName = attributes.getLocalName(i);
 237  6
                 if ("".equals(attributeName)) {
 238  6
                     attributeName = attributes.getQName(i);
 239  
                 }
 240  6
                 String attributeValue = attributes.getValue(i);
 241  6
                 if (attributeName.equalsIgnoreCase(SYNCH_INTERVAL_STR)) {
 242  
                     try {
 243  6
                         synchronizeInterval = Long.parseLong(attributeValue);
 244  0
                     } catch (NumberFormatException nfe) {
 245  0
                         synchronizeInterval = 0;
 246  6
                     }
 247  
                 }
 248  
             }
 249  6
         }
 250  
 
 251  
         /**
 252  
          * <p>Process a node tag</p>
 253  
          *
 254  
          * @param attributes - sax Attributes to process.
 255  
          */
 256  
         private void processNodeTag(Attributes attributes) {
 257  12
             byte[] node = null;
 258  12
             long lastTS = 0;
 259  12
             short lastClockSeq = 0;
 260  48
             for (int i = 0; i < attributes.getLength(); i++) {
 261  36
                 String attributeName = attributes.getLocalName(i);
 262  36
                 if ("".equals(attributeName)) {
 263  36
                     attributeName = attributes.getQName(i);
 264  
                 }
 265  36
                 String attributeValue = attributes.getValue(i);
 266  
 
 267  36
                 if (attributeName.equalsIgnoreCase(ATTR_ID_STR)) {
 268  12
                     node = StateHelper.decodeMACAddress(attributeValue);
 269  12
                 } else if (attributeName.equalsIgnoreCase(ATTR_CLOCKSEQ_STR)) {
 270  
                     try {
 271  12
                         lastClockSeq = Short.parseShort(attributeValue);
 272  0
                     } catch (NumberFormatException nfe) {
 273  0
                         lastClockSeq = 0;
 274  12
                     }
 275  0
                 } else if ( attributeName.equalsIgnoreCase(ATTR_LASTIMESTAMP_STR)) {
 276  
                     try {
 277  12
                         lastTS = Long.parseLong(attributeValue);
 278  0
                     } catch (NumberFormatException nfe) {
 279  0
                         lastTS = 0;
 280  12
                     }
 281  
                 }
 282  
             }
 283  12
             if (node != null) {
 284  12
                 if (lastClockSeq != 0) {
 285  12
                     nodes.add(new Node(node, lastTS, lastClockSeq));
 286  12
                 } else {
 287  0
                     nodes.add(new Node(node));
 288  
                 }
 289  
             }
 290  12
         }
 291  
     }
 292  
     //--------------------------------------------------------------------------
 293  
 }