/**
* Licensed to Apereo under one or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information regarding copyright ownership. Apereo
* 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 the
* following location:
*
* <p>http://www.apache.org/licenses/LICENSE-2.0
*
* <p>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.apereo.portal.utils;
import java.io.IOException;
import java.io.InputStream;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apereo.portal.utils.threading.SingletonDoubleCheckedCreator;
import org.w3c.dom.Document;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
/**
* Produces an empty Document implementation
*
*/
public final class DocumentFactory {
private static final Log log = LogFactory.getLog(DocumentFactory.class);
protected static final SingletonDoubleCheckedCreator<DocumentBuilderFactory>
documentBuilderFactoryInstance = new DocumentBuilderFactoryCreator();
protected static final ThreadLocal<DocumentBuilder> localDocumentBuilder =
new DocumentBuilderLocal();
/**
* Returns a new copy of a Document implementation. This will return an <code>IPortalDocument
* </code> implementation.
*
* @return an empty org.w3c.dom.Document implementation
*/
public static Document getThreadDocument() {
return getThreadDocumentBuilder().newDocument();
}
public static Document getDocumentFromStream(InputStream stream, String publicId)
throws IOException, SAXException {
DocumentBuilder builder = getThreadDocumentBuilder();
InputSource source = new InputSource(stream);
source.setPublicId(publicId);
Document doc = builder.parse(source);
return doc;
}
public static Document getDocumentFromStream(
InputStream stream, EntityResolver er, String publicId)
throws IOException, SAXException {
DocumentBuilder builder = getThreadDocumentBuilder();
builder.setEntityResolver(er);
InputSource source = new InputSource(stream);
source.setPublicId(publicId);
Document doc = builder.parse(source);
return doc;
}
/**
* @return The DocumentBuilder for the current thread. The returned references should NEVER be
* retained outside of the stack.
*/
public static DocumentBuilder getThreadDocumentBuilder() {
return localDocumentBuilder.get();
}
private static final class DocumentBuilderFactoryCreator
extends SingletonDoubleCheckedCreator<DocumentBuilderFactory> {
@Override
protected DocumentBuilderFactory createSingleton(Object... args) {
final DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
dbFactory.setNamespaceAware(true);
dbFactory.setValidating(false);
return dbFactory;
}
}
private static final class DocumentBuilderLocal extends ThreadLocal<DocumentBuilder> {
@Override
protected DocumentBuilder initialValue() {
final DocumentBuilderFactory documentBuilderFactory =
documentBuilderFactoryInstance.get();
try {
return documentBuilderFactory.newDocumentBuilder();
} catch (ParserConfigurationException e) {
throw new IllegalStateException(
"Failed to create new DocumentBuilder for thread: "
+ Thread.currentThread().getName(),
e);
}
}
@Override
public DocumentBuilder get() {
DocumentBuilder documentBuilder = super.get();
//Handle a DocumentBuilder not getting created correctly
if (documentBuilder == null) {
log.warn(
"No DocumentBuilder existed for this thread, an initialValue() call must have failed");
documentBuilder = this.initialValue();
this.set(documentBuilder);
}
return documentBuilder;
}
}
}