Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
ReadOnlyResourceStateImpl |
|
| 3.5;3.5 | ||||
ReadOnlyResourceStateImpl$StateConfigHandler |
|
| 3.5;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 "commons.uuid.configFileName" | |
68 | * (default is "uuid.conf") 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 "commons.uuid.configFileName" | |
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 | } |