Author: veithen
Date: Fri May 21 19:54:39 2010
New Revision: 947146
URL: http://svn.apache.org/viewvc?rev=947146&view=rev
Log:
* WSCOMMONS-541 / AXIS2-4527: Added a clean implementation of a unique ID
generator for MIME content IDs. It guarantees uniqueness and minimizes
(synchronization) overhead, but doesn't guarantee randomness.
* Fixed some related TODO items.
Added:
webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/util/UIDGenerator.java
(with props)
webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/util/UIDGeneratorImpl.java
(with props)
webservices/commons/trunk/modules/axiom/modules/axiom-api/src/test/java/org/apache/axiom/util/UIDGeneratorTest.java
(with props)
Modified:
webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/OMOutputFormat.java
webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/impl/OMStAXWrapper.java
webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/util/stax/xop/ContentIDGenerator.java
webservices/commons/trunk/modules/axiom/modules/axiom-dom/src/main/java/org/apache/axiom/om/impl/dom/TextNodeImpl.java
webservices/commons/trunk/modules/axiom/modules/axiom-impl/src/main/java/org/apache/axiom/om/impl/llom/OMTextImpl.java
webservices/commons/trunk/modules/axiom/modules/axiom-tests/src/test/java/org/apache/axiom/util/stax/xop/XOPRoundtripTest.java
Modified:
webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/OMOutputFormat.java
URL:
http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/OMOutputFormat.java?rev=947146&r1=947145&r2=947146&view=diff
==============================================================================
---
webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/OMOutputFormat.java
(original)
+++
webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/OMOutputFormat.java
Fri May 21 19:54:39 2010
@@ -25,6 +25,7 @@ import org.apache.axiom.om.impl.MTOMCons
import org.apache.axiom.om.util.UUIDGenerator;
import org.apache.axiom.soap.SOAP11Constants;
import org.apache.axiom.soap.SOAP12Constants;
+import org.apache.axiom.util.UIDGenerator;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -191,20 +192,14 @@ public class OMOutputFormat {
public String getRootContentId() {
if (rootContentId == null) {
- rootContentId =
- "0."
- + UUIDGenerator.getUUID()
- + "@apache.org";
+ rootContentId = "0." + UIDGenerator.generateContentId();
}
return rootContentId;
}
public String getNextContentId() {
nextid++;
- return nextid
- + "."
- + UUIDGenerator.getUUID()
- + "@apache.org";
+ return nextid + "." + UIDGenerator.generateContentId();
}
/**
Modified:
webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/impl/OMStAXWrapper.java
URL:
http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/impl/OMStAXWrapper.java?rev=947146&r1=947145&r2=947146&view=diff
==============================================================================
---
webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/impl/OMStAXWrapper.java
(original)
+++
webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/impl/OMStAXWrapper.java
Fri May 21 19:54:39 2010
@@ -22,7 +22,6 @@ package org.apache.axiom.om.impl;
import java.io.IOException;
import javax.activation.DataHandler;
-import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.util.StreamReaderDelegate;
@@ -32,7 +31,6 @@ import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMException;
import org.apache.axiom.om.OMXMLParserWrapper;
import org.apache.axiom.om.OMXMLStreamReader;
-import org.apache.axiom.om.util.UUIDGenerator;
import org.apache.axiom.util.stax.xop.ContentIDGenerator;
import org.apache.axiom.util.stax.xop.OptimizationPolicy;
import org.apache.axiom.util.stax.xop.XOPEncodingStreamReader;
@@ -47,18 +45,6 @@ import org.apache.commons.logging.LogFac
public class OMStAXWrapper extends StreamReaderDelegate implements
OMXMLStreamReader {
private static final Log log = LogFactory.getLog(OMStAXWrapper.class);
- private static final ContentIDGenerator contentIDGenerator = new
ContentIDGenerator() {
- public String generateContentID(String existingContentID) {
- if (existingContentID == null) {
- // TODO: This is what we do in OMTextImpl#getContentID(); note
that this doesn't
- // generate a content ID that strictly conforms to the
specs
- return UUIDGenerator.getUUID() + "@apache.org";
- } else {
- return existingContentID;
- }
- }
- };
-
private final SwitchingWrapper switchingWrapper;
private XOPEncodingStreamReader xopEncoder;
@@ -106,7 +92,7 @@ public class OMStAXWrapper extends Strea
// Since the intention is to support an efficient way to pass
binary content to a
// consumer that is not aware of our data handler extension
(see WSCOMMONS-344), we
// use OptimizationPolicy.ALL, i.e. we ignore
OMText#isOptimized().
- xopEncoder = new XOPEncodingStreamReader(switchingWrapper,
contentIDGenerator,
+ xopEncoder = new XOPEncodingStreamReader(switchingWrapper,
ContentIDGenerator.DEFAULT,
OptimizationPolicy.ALL);
setParent(xopEncoder);
}
Added:
webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/util/UIDGenerator.java
URL:
http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/util/UIDGenerator.java?rev=947146&view=auto
==============================================================================
---
webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/util/UIDGenerator.java
(added)
+++
webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/util/UIDGenerator.java
Fri May 21 19:54:39 2010
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.axiom.util;
+
+public final class UIDGenerator {
+ private static final ThreadLocal impl = new ThreadLocal() {
+ protected Object initialValue() {
+ return new UIDGeneratorImpl();
+ }
+ };
+
+ private UIDGenerator() {}
+
+ /**
+ * Generates a unique ID suitable for usage as a MIME content ID.
+ * <p>
+ * RFC2045 (MIME) specifies that the value of the <tt>Content-ID</tt>
header
+ * must match the <tt>msg-id</tt> production, which is defined by RFC2822
as
+ * follows:
+ * <pre>
+ * msg-id = [CFWS] "<" id-left "@" id-right ">" [CFWS]
+ * id-left = dot-atom-text / no-fold-quote / obs-id-left
+ * id-right = dot-atom-text / no-fold-literal / obs-id-right
+ * dot-atom-text = 1*atext *("." 1*atext)
+ * atext = ALPHA / DIGIT / "!" / "#" / "$" / "%" / "&"
+ * / "'" / "*" / "+" / "-" / "/" / "=" / "?"
+ * / "^" / "_" / "`" / "{" / "|" / "}" / "~"</pre>
+ * In addition, RFC2111 specifies that when used in an URL with scheme
+ * "cid:", the content ID must be URL encoded. Since not all
implementations
+ * handle this correctly, any characters considered "unsafe" in an URL (and
+ * requiring encoding) should be avoided in a content ID.
+ * <p>
+ * This method generates content IDs that satisfy these requirements. It
+ * guarantees a high level of uniqueness, but makes no provisions to
+ * guarantee randomness. The implementation is thread safe, but doesn't use
+ * synchronization.
+ *
+ * @return The generated content ID. Note that this value does not include
+ * the angle brackets of the <tt>msg-id</tt> production, but only
+ * represents the bare content ID.
+ */
+ public static String generateContentId() {
+ StringBuffer buffer = new StringBuffer();
+ ((UIDGeneratorImpl)impl.get()).generateHex(buffer);
+ buffer.append("@apache.org");
+ return buffer.toString();
+ }
+}
Propchange:
webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/util/UIDGenerator.java
------------------------------------------------------------------------------
svn:eol-style = native
Added:
webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/util/UIDGeneratorImpl.java
URL:
http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/util/UIDGeneratorImpl.java?rev=947146&view=auto
==============================================================================
---
webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/util/UIDGeneratorImpl.java
(added)
+++
webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/util/UIDGeneratorImpl.java
Fri May 21 19:54:39 2010
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.axiom.util;
+
+import java.util.Random;
+
+/**
+ * Unique ID generator implementation. This class generates unique IDs based on
+ * the assumption that the following triplet is unique:
+ * <ol>
+ * <li>The thread ID.
+ * <li>The timestamp in milliseconds when the first UID is requested by the
+ * thread.
+ * <li>A per thread sequence number that is incremented each time a UID is
+ * requested by the thread.
+ * </ol>
+ * <p>
+ * Considering that these three numbers are represented as <code>long</code>
+ * values, these assumptions are correct because:
+ * <ul>
+ * <li>The probability that two different threads with the same ID exist in the
+ * same millisecond interval is negligibly small.
+ * <li>One can expect that no thread will ever request more than 2^64 UIDs
+ * during its lifetime.
+ * </ul>
+ * <p>
+ * Before building an ID from this triplet, the implementation will XOR the
+ * three values with random values calculated once when the class is loaded.
+ * This transformation preserves the uniqueness of the calculated triplet and
+ * serves several purposes:
+ * <ul>
+ * <li>It reduces the probability that the same ID is produces by two different
+ * systems, i.e. it increases global uniqueness.
+ * <li>It adds entropy, i.e. it makes an individual ID appear as random.
Indeed,
+ * without the XOR transformation, a hexadecimal representation of the triplet
+ * would in general contain several sequences of '0'.
+ * <li>It prevents the implementation from leaking information about the system
+ * state.
+ * </ul>
+ */
+class UIDGeneratorImpl {
+ private static final long startTimeXorOperand;
+ private static final long threadIdXorOperand;
+ private static final long seqXorOperand;
+
+ static {
+ Random rand = new Random();
+ threadIdXorOperand = rand.nextLong();
+ startTimeXorOperand = rand.nextLong();
+ seqXorOperand = rand.nextLong();
+ }
+
+ private final long xoredThreadId;
+ private final long xoredStartTime;
+ private long seq;
+
+ UIDGeneratorImpl() {
+ xoredThreadId = Thread.currentThread().getId() ^ threadIdXorOperand;
+ xoredStartTime = System.currentTimeMillis() ^ startTimeXorOperand;
+ }
+
+ private void writeReverseLongHex(long value, StringBuffer buffer) {
+ for (int i=0; i<16; i++) {
+ int n = (int)(value >> (4*i)) & 0xF;
+ buffer.append((char)(n < 10 ? '0' + n : 'a' + n - 10));
+ }
+ }
+
+ /**
+ * Generate a unique ID as hex value and add it to the given buffer. Note
+ * that with respect to the triplet, the order of nibbles is reversed, i.e.
+ * the least significant nibble of the sequence is written first. This
makes
+ * comparing two IDs for equality more efficient.
+ *
+ * @param buffer
+ */
+ void generateHex(StringBuffer buffer) {
+ writeReverseLongHex(seq++ ^ seqXorOperand, buffer);
+ writeReverseLongHex(xoredStartTime, buffer);
+ writeReverseLongHex(xoredThreadId, buffer);
+ }
+}
\ No newline at end of file
Propchange:
webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/util/UIDGeneratorImpl.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified:
webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/util/stax/xop/ContentIDGenerator.java
URL:
http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/util/stax/xop/ContentIDGenerator.java?rev=947146&r1=947145&r2=947146&view=diff
==============================================================================
---
webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/util/stax/xop/ContentIDGenerator.java
(original)
+++
webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/util/stax/xop/ContentIDGenerator.java
Fri May 21 19:54:39 2010
@@ -19,6 +19,8 @@
package org.apache.axiom.util.stax.xop;
+import org.apache.axiom.util.UIDGenerator;
+
/**
* Content ID generator interface. Implementations of this interface are used
by
* {...@link XOPEncodingStreamReader} to generate content IDs for use in
<tt>xop:Include</tt>
@@ -26,6 +28,19 @@ package org.apache.axiom.util.stax.xop;
*/
public interface ContentIDGenerator {
/**
+ * Default content ID generator that preserves any existing content ID.
+ */
+ ContentIDGenerator DEFAULT = new ContentIDGenerator() {
+ public String generateContentID(String existingContentID) {
+ if (existingContentID == null) {
+ return UIDGenerator.generateContentId();
+ } else {
+ return existingContentID;
+ }
+ }
+ };
+
+ /**
* Generate a content ID.
*
* @param existingContentID
Added:
webservices/commons/trunk/modules/axiom/modules/axiom-api/src/test/java/org/apache/axiom/util/UIDGeneratorTest.java
URL:
http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-api/src/test/java/org/apache/axiom/util/UIDGeneratorTest.java?rev=947146&view=auto
==============================================================================
---
webservices/commons/trunk/modules/axiom/modules/axiom-api/src/test/java/org/apache/axiom/util/UIDGeneratorTest.java
(added)
+++
webservices/commons/trunk/modules/axiom/modules/axiom-api/src/test/java/org/apache/axiom/util/UIDGeneratorTest.java
Fri May 21 19:54:39 2010
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.axiom.util;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+import junit.framework.TestCase;
+
+public class UIDGeneratorTest extends TestCase {
+ public void testGenerateContentIdFormat() {
+ // This is actually a bit more restrictive than necessary
+ assertTrue(Pattern.matches("\\w+(\\.\\w+)*...@\\w+(\\.\\w+)",
UIDGenerator.generateContentId()));
+ }
+
+ public void testGenerateContentIdUniqueness() {
+ // Not very sophisticated, but should catch stupid regressions
+ Set values = new HashSet();
+ for (int i=0; i<1000; i++) {
+ assertTrue(values.add(UIDGenerator.generateContentId()));
+ }
+ }
+}
Propchange:
webservices/commons/trunk/modules/axiom/modules/axiom-api/src/test/java/org/apache/axiom/util/UIDGeneratorTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified:
webservices/commons/trunk/modules/axiom/modules/axiom-dom/src/main/java/org/apache/axiom/om/impl/dom/TextNodeImpl.java
URL:
http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-dom/src/main/java/org/apache/axiom/om/impl/dom/TextNodeImpl.java?rev=947146&r1=947145&r2=947146&view=diff
==============================================================================
---
webservices/commons/trunk/modules/axiom/modules/axiom-dom/src/main/java/org/apache/axiom/om/impl/dom/TextNodeImpl.java
(original)
+++
webservices/commons/trunk/modules/axiom/modules/axiom-dom/src/main/java/org/apache/axiom/om/impl/dom/TextNodeImpl.java
Fri May 21 19:54:39 2010
@@ -30,7 +30,7 @@ import org.apache.axiom.om.OMText;
import org.apache.axiom.om.OMXMLParserWrapper;
import org.apache.axiom.om.impl.OMNamespaceImpl;
import org.apache.axiom.om.impl.builder.XOPBuilder;
-import org.apache.axiom.om.util.UUIDGenerator;
+import org.apache.axiom.util.UIDGenerator;
import org.apache.axiom.util.base64.Base64Utils;
import org.apache.axiom.util.stax.XMLStreamWriterUtils;
import org.w3c.dom.DOMException;
@@ -404,7 +404,7 @@ public abstract class TextNodeImpl exten
public String getContentID() {
if (contentID == null) {
- contentID = UUIDGenerator.getUUID() + "@apache.org";
+ contentID = UIDGenerator.generateContentId();
}
return this.contentID;
}
Modified:
webservices/commons/trunk/modules/axiom/modules/axiom-impl/src/main/java/org/apache/axiom/om/impl/llom/OMTextImpl.java
URL:
http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-impl/src/main/java/org/apache/axiom/om/impl/llom/OMTextImpl.java?rev=947146&r1=947145&r2=947146&view=diff
==============================================================================
---
webservices/commons/trunk/modules/axiom/modules/axiom-impl/src/main/java/org/apache/axiom/om/impl/llom/OMTextImpl.java
(original)
+++
webservices/commons/trunk/modules/axiom/modules/axiom-impl/src/main/java/org/apache/axiom/om/impl/llom/OMTextImpl.java
Fri May 21 19:54:39 2010
@@ -30,7 +30,7 @@ import org.apache.axiom.om.OMText;
import org.apache.axiom.om.OMXMLParserWrapper;
import org.apache.axiom.om.impl.OMNamespaceImpl;
import org.apache.axiom.om.impl.builder.XOPBuilder;
-import org.apache.axiom.om.util.UUIDGenerator;
+import org.apache.axiom.util.UIDGenerator;
import org.apache.axiom.util.base64.Base64Utils;
import org.apache.axiom.util.stax.XMLStreamWriterUtils;
@@ -394,8 +394,7 @@ public class OMTextImpl extends OMNodeIm
public String getContentID() {
if (contentID == null) {
- contentID = UUIDGenerator.getUUID()
- + "@apache.org";
+ contentID = UIDGenerator.generateContentId();
}
return this.contentID;
}
Modified:
webservices/commons/trunk/modules/axiom/modules/axiom-tests/src/test/java/org/apache/axiom/util/stax/xop/XOPRoundtripTest.java
URL:
http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-tests/src/test/java/org/apache/axiom/util/stax/xop/XOPRoundtripTest.java?rev=947146&r1=947145&r2=947146&view=diff
==============================================================================
---
webservices/commons/trunk/modules/axiom/modules/axiom-tests/src/test/java/org/apache/axiom/util/stax/xop/XOPRoundtripTest.java
(original)
+++
webservices/commons/trunk/modules/axiom/modules/axiom-tests/src/test/java/org/apache/axiom/util/stax/xop/XOPRoundtripTest.java
Fri May 21 19:54:39 2010
@@ -30,23 +30,9 @@ import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMFactory;
import org.apache.axiom.om.OMText;
import org.apache.axiom.om.impl.builder.StAXOMBuilder;
-import org.apache.axiom.om.util.UUIDGenerator;
import org.apache.axiom.util.activation.TestDataSource;
public class XOPRoundtripTest extends TestCase {
- // TODO: copy & paste from OMStAXWrapper; needs to be clarified
- private static final ContentIDGenerator contentIDGenerator = new
ContentIDGenerator() {
- public String generateContentID(String existingContentID) {
- if (existingContentID == null) {
- // TODO: This is what we do in OMTextImpl#getContentID(); note
that this doesn't
- // generate a content ID that strictly conforms to the
specs
- return UUIDGenerator.getUUID() + "@apache.org";
- } else {
- return existingContentID;
- }
- }
- };
-
public void test() {
OMFactory factory = OMAbstractFactory.getOMFactory();
DataHandler dh = new DataHandler(new TestDataSource('x',
Runtime.getRuntime().maxMemory()));
@@ -54,7 +40,7 @@ public class XOPRoundtripTest extends Te
element1.addChild(factory.createOMText(dh, true));
XMLStreamReader originalReader = element1.getXMLStreamReader();
XOPEncodingStreamReader encodedReader = new
XOPEncodingStreamReader(originalReader,
- contentIDGenerator, OptimizationPolicy.DEFAULT);
+ ContentIDGenerator.DEFAULT, OptimizationPolicy.DEFAULT);
XMLStreamReader decodedReader = new
XOPDecodingStreamReader(encodedReader, encodedReader);
OMElement element2 = new
StAXOMBuilder(decodedReader).getDocumentElement();
OMText child = (OMText)element2.getFirstOMChild();