Skip to content

Commit cb8870b

Browse files
committed
fix(#185): remove synchronization at all
1 parent 48854d6 commit cb8870b

3 files changed

Lines changed: 47 additions & 126 deletions

File tree

src/main/java/com/jcabi/xml/XMLDocument.java

Lines changed: 41 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
import javax.xml.xpath.XPathConstants;
6060
import javax.xml.xpath.XPathExpressionException;
6161
import javax.xml.xpath.XPathFactory;
62+
import net.sf.saxon.xpath.XPathFactoryImpl;
6263
import org.w3c.dom.Document;
6364
import org.w3c.dom.Node;
6465
import org.w3c.dom.NodeList;
@@ -80,25 +81,6 @@
8081
}
8182
)
8283
public final class XMLDocument implements XML {
83-
84-
/**
85-
* XPath factory.
86-
*/
87-
private static final XPathFactory XFACTORY =
88-
XPathFactory.newInstance();
89-
90-
/**
91-
* Transformer factory.
92-
*/
93-
private static final TransformerFactory TFACTORY =
94-
TransformerFactory.newInstance();
95-
96-
/**
97-
* DOM document builder factory.
98-
*/
99-
private static final DocumentBuilderFactory DFACTORY =
100-
DocumentBuilderFactory.newInstance();
101-
10284
/**
10385
* Namespace context to use for {@link #xpath(String)}
10486
* and {@link #nodes(String)} methods.
@@ -115,25 +97,6 @@ public final class XMLDocument implements XML {
11597
*/
11698
private final transient Node cache;
11799

118-
/**
119-
* Transformer factory to use for {@link #toString()}.
120-
*/
121-
private final transient TransformerFactory tfactory;
122-
123-
static {
124-
if (XMLDocument.DFACTORY.getClass().getName().contains("xerces")) {
125-
try {
126-
XMLDocument.DFACTORY.setFeature(
127-
"http://apache.org/xml/features/nonvalidating/load-external-dtd",
128-
false
129-
);
130-
} catch (final ParserConfigurationException ex) {
131-
throw new IllegalStateException(ex);
132-
}
133-
}
134-
XMLDocument.DFACTORY.setNamespaceAware(true);
135-
}
136-
137100
/**
138101
* Public ctor, from XML as a text.
139102
*
@@ -154,40 +117,12 @@ public final class XMLDocument implements XML {
154117
*/
155118
public XMLDocument(final String text) {
156119
this(
157-
new DomParser(XMLDocument.DFACTORY, text).document(),
120+
new DomParser(XMLDocument.configuredDFactory(), text).document(),
158121
new XPathContext(),
159122
false
160123
);
161124
}
162125

163-
/**
164-
* Public ctor, from XML as a text.
165-
*
166-
* <p>The object is created with a default implementation of
167-
* {@link NamespaceContext}, which already defines a
168-
* number of namespaces, for convenience, including:
169-
*
170-
* <pre> xhtml: http://www.w3.org/1999/xhtml
171-
* xs: http://www.w3.org/2001/XMLSchema
172-
* xsi: http://www.w3.org/2001/XMLSchema-instance
173-
* xsl: http://www.w3.org/1999/XSL/Transform
174-
* svg: http://www.w3.org/2000/svg</pre>
175-
*
176-
* <p>In future versions we will add more namespaces (submit a ticket if
177-
* you need more of them defined here).
178-
*
179-
* @param text XML document body
180-
* @param factory Transformer factory
181-
*/
182-
public XMLDocument(final String text, final TransformerFactory factory) {
183-
this(
184-
new DomParser(XMLDocument.DFACTORY, text).document(),
185-
new XPathContext(),
186-
false,
187-
factory
188-
);
189-
}
190-
191126
/**
192127
* Public ctor, from XML as a text.
193128
*
@@ -208,7 +143,7 @@ public XMLDocument(final String text, final TransformerFactory factory) {
208143
*/
209144
public XMLDocument(final byte[] data) {
210145
this(
211-
new DomParser(XMLDocument.DFACTORY, data).document(),
146+
new DomParser(XMLDocument.configuredDFactory(), data).document(),
212147
new XPathContext(),
213148
false
214149
);
@@ -334,43 +269,26 @@ public XMLDocument(final InputStream stream) throws IOException {
334269
stream.close();
335270
}
336271

337-
/**
338-
* Private ctor.
339-
* @param node The source
340-
* @param ctx Namespace context
341-
* @param lfe Is it a leaf node?
342-
*/
343-
private XMLDocument(
344-
final Node node,
345-
final XPathContext ctx,
346-
final boolean lfe
347-
) {
348-
this(node, ctx, lfe, XMLDocument.TFACTORY);
349-
}
350-
351272
/**
352273
* Private ctor.
353274
* @param cache The source
354275
* @param context Namespace context
355276
* @param leaf Is it a leaf node?
356-
* @param tfactory Transformer factory
357277
* @checkstyle ParameterNumberCheck (5 lines)
358278
*/
359-
public XMLDocument(
279+
private XMLDocument(
360280
final Node cache,
361281
final XPathContext context,
362-
final boolean leaf,
363-
final TransformerFactory tfactory
282+
final boolean leaf
364283
) {
365284
this.context = context;
366285
this.leaf = leaf;
367286
this.cache = cache;
368-
this.tfactory = tfactory;
369287
}
370288

371289
@Override
372290
public String toString() {
373-
return this.asString(this.cache);
291+
return XMLDocument.asString(this.cache);
374292
}
375293

376294
@Override
@@ -438,7 +356,7 @@ public List<String> xpath(final String query) {
438356
throw new IllegalArgumentException(
439357
String.format(
440358
"Invalid XPath query '%s' at %s: %s",
441-
query, XMLDocument.XFACTORY.getClass().getName(),
359+
query, XPathFactoryImpl.class.getName(),
442360
ex.getLocalizedMessage()
443361
),
444362
exp
@@ -475,7 +393,7 @@ public List<XML> nodes(final String query) {
475393
throw new IllegalArgumentException(
476394
String.format(
477395
"Invalid XPath query '%s' by %s",
478-
query, XMLDocument.XFACTORY.getClass().getName()
396+
query, XPathFactoryImpl.class.getName()
479397
), ex
480398
);
481399
}
@@ -497,14 +415,15 @@ public XML merge(final NamespaceContext ctx) {
497415
* @return A cloned node imported in a dedicated document.
498416
*/
499417
private static Node createImportedNode(final Node node) {
418+
final DocumentBuilderFactory factory = XMLDocument.configuredDFactory();
500419
final DocumentBuilder builder;
501420
try {
502-
builder = XMLDocument.DFACTORY.newDocumentBuilder();
421+
builder = factory.newDocumentBuilder();
503422
} catch (final ParserConfigurationException ex) {
504423
throw new IllegalArgumentException(
505424
String.format(
506425
"Failed to create document builder by %s",
507-
XMLDocument.DFACTORY.getClass().getName()
426+
factory.getClass().getName()
508427
),
509428
ex
510429
);
@@ -530,12 +449,9 @@ private static Node createImportedNode(final Node node) {
530449
* @throws XPathExpressionException If an error occurs when evaluating XPath
531450
*/
532451
@SuppressWarnings("unchecked")
533-
private <T> T fetch(final String query, final Class<T> type)
534-
throws XPathExpressionException {
535-
final XPath xpath;
536-
synchronized (XMLDocument.class) {
537-
xpath = XMLDocument.XFACTORY.newXPath();
538-
}
452+
private <T> T fetch(final String query, final Class<T> type) throws XPathExpressionException {
453+
final XPathFactory factory = XPathFactory.newInstance();
454+
final XPath xpath = factory.newXPath();
539455
xpath.setNamespaceContext(this.context);
540456
final QName qname;
541457
if (type.equals(String.class)) {
@@ -559,18 +475,16 @@ private <T> T fetch(final String query, final Class<T> type)
559475
* @param node The DOM node.
560476
* @return String representation
561477
*/
562-
private String asString(final Node node) {
563-
final StringWriter writer = new StringWriter();
478+
private static String asString(final Node node) {
479+
final TransformerFactory factory = TransformerFactory.newInstance();
564480
final Transformer trans;
565481
try {
566-
synchronized (XMLDocument.class) {
567-
trans = this.tfactory.newTransformer();
568-
}
482+
trans = factory.newTransformer();
569483
} catch (final TransformerConfigurationException ex) {
570484
throw new IllegalArgumentException(
571485
String.format(
572486
"Failed to create transformer by %s",
573-
this.tfactory.getClass().getName()
487+
XPathFactoryImpl.class.getName()
574488
),
575489
ex
576490
);
@@ -581,11 +495,10 @@ private String asString(final Node node) {
581495
trans.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
582496
}
583497
final Source source = new DOMSource(node);
498+
final StringWriter writer = new StringWriter();
584499
final Result result = new StreamResult(writer);
585500
try {
586-
synchronized (node) {
587-
trans.transform(source, result);
588-
}
501+
trans.transform(source, result);
589502
} catch (final TransformerException ex) {
590503
throw new IllegalArgumentException(
591504
String.format(
@@ -606,11 +519,9 @@ private String asString(final Node node) {
606519
*/
607520
private static Node transform(final Source source) {
608521
final DOMResult result = new DOMResult();
522+
final TransformerFactory factory = TransformerFactory.newInstance();
609523
try {
610-
final Transformer trans;
611-
synchronized (XMLDocument.class) {
612-
trans = XMLDocument.TFACTORY.newTransformer();
613-
}
524+
final Transformer trans = factory.newTransformer();
614525
trans.transform(source, result);
615526
} catch (final TransformerException ex) {
616527
throw new IllegalArgumentException(
@@ -625,4 +536,23 @@ private static Node transform(final Source source) {
625536
return result.getNode();
626537
}
627538

539+
/**
540+
* Create new {@link DocumentBuilderFactory} and configure it.
541+
* @return Configured factory
542+
*/
543+
private static DocumentBuilderFactory configuredDFactory() {
544+
final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
545+
if (factory.getClass().getName().contains("xerces")) {
546+
try {
547+
factory.setFeature(
548+
"http://apache.org/xml/features/nonvalidating/load-external-dtd",
549+
false
550+
);
551+
} catch (final ParserConfigurationException ex) {
552+
throw new IllegalStateException(ex);
553+
}
554+
}
555+
factory.setNamespaceAware(true);
556+
return factory;
557+
}
628558
}

src/main/java/com/jcabi/xml/XSDDocument.java

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -177,11 +177,9 @@ public String toString() {
177177
public Collection<SAXParseException> validate(final Source xml) {
178178
final Schema schema;
179179
try {
180-
synchronized (XSDDocument.class) {
181-
schema = SchemaFactory
182-
.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI)
183-
.newSchema(new StreamSource(new StringReader(this.xsd)));
184-
}
180+
schema = SchemaFactory
181+
.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI)
182+
.newSchema(new StreamSource(new StringReader(this.xsd)));
185183
} catch (final SAXException ex) {
186184
throw new IllegalStateException(
187185
String.format("Failed to create XSD schema from %s", this.xsd),
@@ -193,9 +191,7 @@ public Collection<SAXParseException> validate(final Source xml) {
193191
final Validator validator = schema.newValidator();
194192
validator.setErrorHandler(new XSDDocument.ValidationHandler(errors));
195193
try {
196-
synchronized (XSDDocument.class) {
197-
validator.validate(xml);
198-
}
194+
validator.validate(xml);
199195
} catch (final SAXException | IOException ex) {
200196
throw new IllegalStateException(ex);
201197
}

src/test/java/com/jcabi/xml/XMLDocumentTest.java

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@
4040
import java.util.concurrent.Executors;
4141
import java.util.concurrent.TimeUnit;
4242
import java.util.concurrent.atomic.AtomicInteger;
43-
import javax.xml.transform.TransformerFactory;
4443
import org.apache.commons.lang3.StringUtils;
4544
import org.cactoos.io.ResourceOf;
4645
import org.cactoos.io.TeeInput;
@@ -391,15 +390,11 @@ void comparesDocumentsWithDifferentIndentations() {
391390
// The current implementation of XMLDocument does not ignore different indentations
392391
// when comparing two XML documents. We need to implement a comparison that ignores
393392
// different indentations. Don't forget to remove the @Disabled annotation from this test.
394-
final TransformerFactory factory = TransformerFactory.newInstance(
395-
"com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl",
396-
Thread.currentThread().getContextClassLoader()
397-
);
398393
MatcherAssert.assertThat(
399394
"Different indentations should be ignored",
400-
new XMLDocument("<program>\n <indentation/>\n</program>", factory),
395+
new XMLDocument("<program>\n <indentation/>\n</program>"),
401396
Matchers.equalTo(
402-
new XMLDocument("<program>\n <indentation/>\n</program>\n", factory)
397+
new XMLDocument("<program>\n <indentation/>\n</program>\n")
403398
)
404399
);
405400
}

0 commit comments

Comments
 (0)