xom-interest AT lists.ibiblio.org
Subject: XOM API for Processing XML with Java
List archive
- From: Wolfgang Hoschek <whoschek AT lbl.gov>
- To: xom-interest AT lists.ibiblio.org
- Subject: [XOM-interest] Re: EntityResolver
- Date: Sat, 28 Aug 2004 11:33:51 -0700
At 6:25 PM +0200 8/23/04, Peter van der Winkel wrote:
Hello,
How can I give my custom EntityResolver to XOM ?
Set it on the XMLReader, and use the XMLReader to create the Builder.
The Builder constructor API is only convenient for simple/basic use cases. For anything beyond that you'r on your own.
Here is our SchemaValidatingBuilderFactory class for DTDs and W3C XML Schemas that probably does what you need in a robust, convenient and efficient manner. You can also use this in conjunction with the BuilderPool class (also attached).
Contact me if you'd like test drivers/benchmarks for these things.
We'd be open to propose to contribute these classes to XOM if there's sufficient interest and they'd be accepted by the XOM maintainer.
Regards,
Wolfgang.
-----------------------------------------------------------------------
Wolfgang Hoschek | email: whoschek AT lbl.gov
Distributed Systems Department | phone: (415)-533-7610
Berkeley Laboratory | http://dsd.lbl.gov/~hoschek/
-----------------------------------------------------------------------
/*
* Copyright (c) 2003, The Regents of the University of California, through
* Lawrence Berkeley National Laboratory (subject to receipt of any required
* approvals from the U.S. Dept. of Energy). All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* (1) Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* (2) Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* (3) Neither the name of the University of California, Lawrence Berkeley
* National Laboratory, U.S. Dept. of Energy nor the names of its contributors
* may be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* You are under no obligation whatsoever to provide any bug fixes, patches, or
* upgrades to the features, functionality or performance of the source code
* ("Enhancements") to anyone; however, if you choose to make your Enhancements
* available either publicly, or directly to Lawrence Berkeley National
* Laboratory, without imposing a separate written license agreement for such
* Enhancements, then you hereby grant the following license: a non-exclusive,
* royalty-free perpetual license to install, use, modify, prepare derivative
* works, incorporate into other computer software, distribute, and sublicense
* such enhancements or derivative works thereof, in binary and source code
* form.
*/
package nu.xom.contrib;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import nu.xom.Builder;
import nu.xom.XMLException;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXNotRecognizedException;
import org.xml.sax.SAXNotSupportedException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;
/**
* Creates and returns a new <code>Builder</code> that validates against a DTD or W3C XML Schema.
* <p>
* Example usage:
* <pre>
* Builder builder = new SchemaValidatingBuilderFactory().createW3CBuilder("file:/tmp/ p2pio.xsd", "http://dsd.lbl.gov/p2pio-1.0");
* Document doc = builder.build(new File("/tmp/test.xml"));
* System.out.println(doc.toXML());
* </pre>
*
* This class might more naturally be integrated into the Builder class itself,
* appearing as <code>new Builder(schema, namespace)</code> or
* <code>new Builder(schemaLanguage, schema, namespace)</code> or similar.
*
* @author whoschek AT lbl.gov
* @author $Author: hoschek3 $
* @version $Revision: 1.8 $, $Date: 2004/05/24 20:56:16 $
*/
public class SchemaValidatingBuilderFactory {
private static final boolean wantReusableParser = true;
// These are stored in the order of preference.
private static final String[] schemaParsers = {
"org.apache.xerces.parsers.SAXParser",
"com.sun.org.apache.xerces.internal.parsers.SAXParser" // JDK
1.5
};
// These are stored in the order of preference.
private static final String[] schemaGrammarPools = {
"org.apache.xerces.util.XMLGrammarPoolImpl",
"com.sun.org.apache.xerces.internal.util.XMLGrammarPoolImpl" // JDK 1.5
};
/**
* Creates a factory instance.
*/
public SchemaValidatingBuilderFactory() {}
/**
* Creates and returns a new <code>Builder</code> that validates against a W3C XML Schema.
*
* @param schema - the location URL of the external schema (may be null)
* @param namespace - the namespace URI of the schema (may be null)
*
* @throws XMLException
* if no satisfactory parser is found in the class path
*/
public Builder createW3CBuilder(String schema, String namespace) {
XMLReader parser = findParser();
try {
setupParser(parser, schema, namespace);
} catch (SAXException e) {
throw new XMLException("Can't create schema validating
parser", e);
}
return new Builder(parser, true);
}
/**
* Creates and returns a new <code>Builder</code> that validates against
* the DTD obtained by the given entity resolver.
* <p>
* Quite possibly, you will want to use this in conjunction with a <a
* target="top" href="http://doctypechanger.sourceforge.net"> DoctypeChanger </a> helper or
* a SAX-2.0.1 EntityResolver2.
*
* @param resolver -
* the entity resolver obtaining the DTD
*
* @throws XMLException
* if no satisfactory parser is found in the class path
*/
public Builder createDTDBuilder(EntityResolver resolver) {
XMLReader parser = findParser();
//parser.setFeature("http://xml.org/sax/features/validation", true); // will be set by XOM anyway
parser.setEntityResolver(resolver);
return new Builder(parser, true);
}
/**
* Creates and returns a new <code>Builder</code> that validates against the
* DTD obtained from the given input stream.
*
* @param in - the input stream for the DTD
*
* @throws XMLException
* if no satisfactory parser is found in the class path
*/
public Builder createDTDBuilder(InputStream in) throws IOException {
// cache data for efficient future reuse, avoiding repeated I/O on reuse,
// in particular network I/O if this is a networked stream.
final byte[] data = readFully(in);
return createDTDBuilder(
new EntityResolver() {
public InputSource resolveEntity(String
publicId, String systemId) {
return new InputSource(new
ByteArrayInputStream(data));
}
}
);
}
private XMLReader findParser() {
// XMLReaderFactory.createXMLReader never returns
// null. If it can't locate the parser, it throws
// a SAXException.
XMLReader parser;
for (int i = 0; i < schemaParsers.length; i++) {
try {
parser =
XMLReaderFactory.createXMLReader(schemaParsers[i]);
if (wantReusableParser) {
// This improves performance and,
more importantly, prevents xerces
// bugs/exceptions when the Builder is used more than once (at least for
// xerces-2.6.2)
// See
http://xml.apache.org/xerces2-j/faq-grammars.html
parser.setProperty("http://apache.org/xml/properties/internal/grammar- pool",
Class.forName(schemaGrammarPools[i]).newInstance());
}
return parser;
} catch (SAXException ex) {
// try the next one
} catch (NoClassDefFoundError err) {
// try the next one
} catch (Exception err) {
// try the next one
}
}
try { // default
// could use JAXP instead to specify schema
parser = XMLReaderFactory.createXMLReader();
return parser;
} catch (SAXException ex) {
throw new XMLException("Could not find a suitable SAX2
parser", ex);
} catch (NoClassDefFoundError ex) {
throw new XMLException("Could not find a suitable SAX2
parser", ex);
}
}
/**
* See http://xml.apache.org/xerces-j/properties.html and
* http://xml.apache.org/xerces-j/features.html
*/
private void setupParser(XMLReader parser, String schema, String namespace)
throws SAXNotRecognizedException, SAXNotSupportedException {
parser.setFeature("http://apache.org/xml/features/validation/schema", true);
//parser.setFeature("http://xml.org/sax/features/validation", true); // will be set by XOM anyway
if (schema != null) {
if (namespace != null) {
parser.setProperty(
"http://apache.org/xml/properties/schema/external-schemaLocation",
namespace + " " + schema);
}
else {
parser.setProperty(
"http://apache.org/xml/properties/schema/external- noNamespaceSchemaLocation",
schema);
}
}
}
private static byte[] readFully(InputStream input) throws IOException
{
ByteArrayOutputStream output = new ByteArrayOutputStream();
byte[] buffer = new byte[4096];
int n;
try {
while ((n = input.read(buffer)) > 0) {
output.write(buffer, 0, n);
}
return output.toByteArray();
} finally {
input.close();
}
}
// make xerces cache the parsed schema grammar;
// this is another way to prevent xerces bugs/exceptions when Builder is used more than once
// System.setProperty("org.apache.xerces.xni.parser.XMLParserConfiguration" ,
// "org.apache.xerces.parsers.XMLGrammarCachingConfiguration");
}
/*
* Copyright (c) 2003, The Regents of the University of California, through
* Lawrence Berkeley National Laboratory (subject to receipt of any required
* approvals from the U.S. Dept. of Energy). All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* (1) Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* (2) Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* (3) Neither the name of the University of California, Lawrence Berkeley
* National Laboratory, U.S. Dept. of Energy nor the names of its contributors
* may be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* You are under no obligation whatsoever to provide any bug fixes, patches, or
* upgrades to the features, functionality or performance of the source code
* ("Enhancements") to anyone; however, if you choose to make your Enhancements
* available either publicly, or directly to Lawrence Berkeley National
* Laboratory, without imposing a separate written license agreement for such
* Enhancements, then you hereby grant the following license: a non-exclusive,
* royalty-free perpetual license to install, use, modify, prepare derivative
* works, incorporate into other computer software, distribute, and sublicense
* such enhancements or derivative works thereof, in binary and source code
* form.
*/
package nu.xom.contrib;
import java.util.LinkedHashMap;
import java.util.Map;
import nu.xom.Builder;
import nu.xom.XMLException;
import org.xml.sax.EntityResolver;
/**
* Efficient thread-safe pool/cache of XOM {@link Builder} objects, creating and
* holding zero or more Builders per thread.
* <p>
* Recognizing that Builders are not thread-safe but can be reused serially,
* this class helps to avoid the large overhead
* involved in creating a Builder instance (more precisely: an underlying
* XMLReader instance), in particul for Builders that validate against W3C XML Schemas.
* Most useful in high throughput server container environments and for small XML documents
* (e.g. large-scale Peer-to-Peer messaging network infrastructures over high-bandwidth networks,
* scalable MOMs, etc).
* <p>
* Configured across the VM through a maxPoolEntries System property. maxPoolEntries
* should not be set to a large value since each Builder (through its XMLReader)
* holds onto some results of the last parsing build(), potentially preventing
* substantial memory from garbage collection.
* Pool eviction is based on a LRU (least recently used) policy.
* <p>
* Example usage (in any arbitrary thread and any arbitrary object):
* <pre>
* public void foo() {
* Document doc = BuilderPool.get(false).build(new File("/tmp/test.xml"));
* //Document doc = new Builder(false).build(new File("/tmp/test.xml")); // would be inefficient
* System.out.println(doc.toXML());
* doc = BuilderPool.get("file:/tmp/p2pio.xsd", "http://dsd.lbl.gov/p2pio-1.0").build(new File("/tmp/test.xml"));
* System.out.println(doc.toXML());
* }
* </pre>
* <p>
* This implementation is completely unsynchronized yet thread-safe.
* <p>
* Requires Xerces (either external for JDK 1.4, or as packaged inside JDK 1.5).
*
* @author whoschek AT lbl.gov
* @author $Author: hoschek3 $
* @version $Revision: 1.4 $, $Date: 2004/02/06 23:05:35 $
*/
public class BuilderPool {
// MAX_POOL_ENTRIES = 0 effectively disables pooling
private static final int MAX_POOL_ENTRIES = Integer.getInteger(
BuilderPool.class.getName() + ".maxPoolEntries",
3).intValue();
/**
* One local map per thread; When a thread terminates and is no more
* referenced its copy of "localMap" (with contained builders) is subject to
* garbage collection.
* <p>
* Static variable is by deliberate design to avoid ThreadLocal memory
* leaks. Hence non-static methods would make little sense.
*/
private static final ThreadLocal localMap = new ThreadLocal() {
protected Object initialValue() { // lazy init
return new LinkedHashMap(1, 0.75f, true) {
protected boolean removeEldestEntry(Map.Entry
eldest) {
return size() > MAX_POOL_ENTRIES;
}
};
//return new HashMap(1); // for JDK 1.3 compatibility
}
};
private BuilderPool() {} // not instantiable
/**
* Returns a <code>Builder</code> based on an optionally validating
* parser. If the <code>validate</code> argument is true, then a validity
* error while parsing will cause a fatal error; that is, it will throw a
* <code>ValidityException</code>.
*
* @param validate
* true if the parser should validate the document while parsing
*
* @throws XMLException
* if no satisfactory parser is found in the class path
*/
public static Builder get(boolean validate) {
Boolean key = Boolean.valueOf(validate); // JDK 1.4
Map map = (Map) localMap.get();
Builder builder = (Builder) map.get(key);
if (builder == null) {
// let XOM do the hard work
builder = new Builder(validate);
map.put(key, builder);
}
return builder;
}
/**
* Returns a <code>Builder</code> that validates against a W3C XML Schema.
*
* @param schema - the location URL of the external schema (may be null)
* @param namespace - the namespace URI of the schema (may be null)
*
* @throws XMLException
* if no satisfactory parser is found in the class path
*/
public static Builder get(String schema, String namespace) {
String key = namespace + "$$$" + schema; // this is unique and well-behaved for all practical purposes
Map map = (Map) localMap.get();
Builder builder = (Builder) map.get(key);
if (builder == null) {
// must do the hard work ourselves since XOM does not
help here
builder = new SchemaValidatingBuilderFactory().createW3CBuilder(schema, namespace);
map.put(key, builder);
}
return builder;
}
/**
* Returns a <code>Builder</code> that validates against the
* DTD obtained by the given entity resolver.
*
* @param resolver - the entity resolver obtaining the DTD
*
* @throws XMLException
* if no satisfactory parser is found in the class path
*/
public static Builder get(EntityResolver resolver) {
EntityResolver key = resolver;
Map map = (Map) localMap.get();
Builder builder = (Builder) map.get(key);
if (builder == null) {
// must do the hard work ourselves since XOM does not
help here
builder = new SchemaValidatingBuilderFactory().createDTDBuilder(resolver);
map.put(key, builder);
}
return builder;
}
// not really needed:
// public static void remove(Builder builder) {
// Map map = (Map) localMap.get();
// map.values().remove(builder);
// }
}
- [XOM-interest] Re: EntityResolver, Wolfgang Hoschek, 08/28/2004
Archive powered by MHonArc 2.6.24.