Author: alessio.soldano(a)jboss.com
Date: 2009-12-23 08:39:00 -0500 (Wed, 23 Dec 2009)
New Revision: 11354
Added:
common/branches/jbossws-common-1.1.0/src/main/java/org/jboss/wsf/common/JBossWSDocumentBuilderFactory.java
common/branches/jbossws-common-1.1.0/src/main/java/org/jboss/wsf/common/SecurityActions.java
common/branches/jbossws-common-1.1.0/src/test/java/org/jboss/test/ws/common/utils/DummyDocumentBuilderFactory.java
common/branches/jbossws-common-1.1.0/src/test/java/org/jboss/test/ws/common/utils/JBossWSDocumentBuilderFactoryTestCase.java
Modified:
common/branches/jbossws-common-1.1.0/src/main/java/org/jboss/wsf/common/DOMUtils.java
Log:
[JBPAPP-3303] Porting changes from trunk + adding SecurityActions utility class
Modified:
common/branches/jbossws-common-1.1.0/src/main/java/org/jboss/wsf/common/DOMUtils.java
===================================================================
---
common/branches/jbossws-common-1.1.0/src/main/java/org/jboss/wsf/common/DOMUtils.java 2009-12-23
13:20:16 UTC (rev 11353)
+++
common/branches/jbossws-common-1.1.0/src/main/java/org/jboss/wsf/common/DOMUtils.java 2009-12-23
13:39:00 UTC (rev 11354)
@@ -1,6 +1,6 @@
/*
* JBoss, Home of Professional Open Source.
- * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * Copyright 2009, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
@@ -80,7 +80,7 @@
DocumentBuilderFactory factory = null;
try
{
- factory = DocumentBuilderFactory.newInstance();
+ factory = JBossWSDocumentBuilderFactory.newInstance();
factory.setValidating(false);
factory.setNamespaceAware(true);
Copied:
common/branches/jbossws-common-1.1.0/src/main/java/org/jboss/wsf/common/JBossWSDocumentBuilderFactory.java
(from rev 11350,
common/trunk/src/main/java/org/jboss/wsf/common/JBossWSDocumentBuilderFactory.java)
===================================================================
---
common/branches/jbossws-common-1.1.0/src/main/java/org/jboss/wsf/common/JBossWSDocumentBuilderFactory.java
(rev 0)
+++
common/branches/jbossws-common-1.1.0/src/main/java/org/jboss/wsf/common/JBossWSDocumentBuilderFactory.java 2009-12-23
13:39:00 UTC (rev 11354)
@@ -0,0 +1,492 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2009, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.wsf.common;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.WeakHashMap;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.validation.Schema;
+
+import org.jboss.logging.Logger;
+
+/**
+ * A thread-safe {@link DocumentBuilderFactory} that also adds a caching system
+ * for preventing useless access to the filesystem due to the Service API when
+ * the same context classloader is in place.
+ *
+ * @author alessio.soldano(a)jboss.com
+ * @since 22-Dec-2009
+ *
+ */
+public class JBossWSDocumentBuilderFactory extends DocumentBuilderFactory
+{
+ private static Logger log = Logger.getLogger(JBossWSDocumentBuilderFactory.class);
+ private static final String PROPERTY_NAME =
"javax.xml.parsers.DocumentBuilderFactory";
+ private static final boolean useJaxpProperty;
+ /**
+ * A weak hash map that keeps DocumentBuilderFactory instances for each classloader.
+ * Weak keys are used to remove entries when classloaders are garbage collected.
+ *
+ * No need for a synchronized map as this accessed from the
+ * static synchronized newInstance newInstance method.
+ */
+ private static Map<ClassLoader, JBossWSDocumentBuilderFactory> factoryMap = new
WeakHashMap<ClassLoader, JBossWSDocumentBuilderFactory>();
+
+ private final DocumentBuilderFactory delegate;
+
+ //ThreadLocal attributes and features maps required to achieve thread safety
+ private ThreadLocal<DocumentBuilderFactoryFields> fields = new
ThreadLocal<DocumentBuilderFactoryFields>() {
+ @Override
+ protected DocumentBuilderFactoryFields initialValue()
+ {
+ return new DocumentBuilderFactoryFields();
+ }
+ };
+
+ static
+ {
+ // Use the properties file "lib/jaxp.properties" in the JRE directory.
+ // This configuration file is in standard java.util.Properties format and contains
the fully
+ // qualified name of the implementation class with the key being the system
property defined above.
+ PrivilegedAction<Object> action = new
PropertyAccessAction("java.home");
+ String javaHome = (String)AccessController.doPrivileged(action);
+ File jaxmFile = new File(javaHome + "/lib/jaxp.properties");
+ if ((Boolean)AccessController.doPrivileged(new PropertyFileExistAction(jaxmFile)))
+ {
+ String factoryName = null;
+ boolean error = false;
+ try
+ {
+ action = new PropertyFileAccessAction(jaxmFile.getCanonicalPath());
+ Properties jaxmProperties =
(Properties)AccessController.doPrivileged(action);
+ factoryName = jaxmProperties.getProperty(PROPERTY_NAME);
+ }
+ catch (IOException e)
+ {
+ log.warn("Can't read " + jaxmFile);
+ error = true;
+ }
+ finally
+ {
+ useJaxpProperty = (error || (factoryName != null));
+ }
+ }
+ else
+ {
+ useJaxpProperty = false;
+ }
+ }
+
+ private JBossWSDocumentBuilderFactory(DocumentBuilderFactory delegate)
+ {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public Object getAttribute(String name) throws IllegalArgumentException
+ {
+ return fields.get().getAttribute(name);
+ }
+
+ @Override
+ public boolean getFeature(String name) throws ParserConfigurationException
+ {
+ return fields.get().getFeature(name);
+ }
+
+ /**
+ * The creation method for the document builder; it's synchronized to allow us
configuring the underlying
+ * DocumentBuilderFactory and delegate to it in a thread safe way.
+ *
+ */
+ @Override
+ public synchronized DocumentBuilder newDocumentBuilder() throws
ParserConfigurationException
+ {
+ DocumentBuilderFactoryFields currentFields = fields.get();
+ currentFields.copyTo(delegate);
+ return delegate.newDocumentBuilder();
+ }
+
+ @Override
+ public void setAttribute(String name, Object value) throws IllegalArgumentException
+ {
+ fields.get().setAttribute(name, value);
+ }
+
+ @Override
+ public void setFeature(String name, boolean value) throws
ParserConfigurationException
+ {
+ fields.get().setFeature(name, value);
+ }
+
+ @Override
+ public boolean isCoalescing()
+ {
+ return fields.get().isCoalescing();
+ }
+
+ @Override
+ public void setCoalescing(boolean coalescing)
+ {
+ fields.get().setCoalescing(coalescing);
+ }
+
+ @Override
+ public boolean isExpandEntityReferences()
+ {
+ return fields.get().isExpandEntityReferences();
+ }
+
+ @Override
+ public void setExpandEntityReferences(boolean expandEntityReferences)
+ {
+ fields.get().setExpandEntityReferences(expandEntityReferences);
+ }
+
+ @Override
+ public boolean isIgnoringComments()
+ {
+ return fields.get().isIgnoringComments();
+ }
+
+ @Override
+ public void setIgnoringComments(boolean ignoringComments)
+ {
+ fields.get().setIgnoringComments(ignoringComments);
+ }
+
+ @Override
+ public boolean isIgnoringElementContentWhitespace()
+ {
+ return fields.get().isIgnoringElementContentWhitespace();
+ }
+
+ @Override
+ public void setIgnoringElementContentWhitespace(boolean
ignoringElementContentWhitespace)
+ {
+
fields.get().setIgnoringElementContentWhitespace(ignoringElementContentWhitespace);
+ }
+
+ @Override
+ public boolean isNamespaceAware()
+ {
+ return fields.get().isNamespaceAware();
+ }
+
+ @Override
+ public void setNamespaceAware(boolean namespaceAware)
+ {
+ fields.get().setNamespaceAware(namespaceAware);
+ }
+
+ @Override
+ public Schema getSchema()
+ {
+ return fields.get().getSchema();
+ }
+
+ @Override
+ public void setSchema(Schema schema)
+ {
+ fields.get().setSchema(schema);
+ }
+
+ @Override
+ public boolean isValidating()
+ {
+ return fields.get().isValidating();
+ }
+
+ @Override
+ public void setValidating(boolean validating)
+ {
+ fields.get().setValidating(validating);
+ }
+
+ @Override
+ public boolean isXIncludeAware()
+ {
+ return fields.get().isXIncludeAware();
+ }
+
+ @Override
+ public void setXIncludeAware(boolean includeAware)
+ {
+ fields.get().setXIncludeAware(includeAware);
+ }
+
+ /**
+ * The {@link DocumentBuilderFactory#newInstance()} documentation defines the
retrieval algorithm:
+ *
+ * 1) Use the javax.xml.parsers.DocumentBuilderFactory system property.
+ * 2) Use the properties file "lib/jaxp.properties" in the JRE directory.
This configuration file is in standard java.util.Properties format
+ * and contains the fully qualified name of the implementation class with the key
being the system property defined above. The jaxp.properties
+ * file is read only once by the JAXP implementation and it's values are then
cached for future use. If the file does not exist when the first
+ * attempt is made to read from it, no further attempts are made to check for its
existence. It is not possible to change the value of any
+ * property in jaxp.properties after it has been read for the first time.
+ * 3) Use the Services API (as detailed in the JAR specification), if available, to
determine the classname. The Services API will look for a
+ * classname in the file META-INF/services/javax.xml.parsers.DocumentBuilderFactory
in jars available to the runtime.
+ * 4) Platform default DocumentBuilderFactory instance.
+ *
+ * So we basically check if 1) or 2) applies: if yes, we simply delegate to the
DocumentBuilderFactory, otherwise we first try using our classloader
+ * cache and delegate to the DocumentBuilderFactory only in case of a miss in the
cache. Then we wrap up the result into a JBossWSDocumentBuilderFactory
+ * instance.
+ *
+ * @return a DocumentBuilderFactoryInstance
+ */
+ public static synchronized JBossWSDocumentBuilderFactory newInstance()
+ {
+ if (useJaxpProperty || getFactoryNameFromSystemProperty() != null)
+ {
+ return new JBossWSDocumentBuilderFactory(DocumentBuilderFactory.newInstance());
+ }
+ ClassLoader classLoader = SecurityActions.getContextClassLoader();
+ JBossWSDocumentBuilderFactory factory = factoryMap.get(classLoader);
+ if (factory == null)
+ {
+ factory = new
JBossWSDocumentBuilderFactory(DocumentBuilderFactory.newInstance());
+ factoryMap.put(classLoader, factory);
+ }
+ return factory;
+ }
+
+ private static String getFactoryNameFromSystemProperty()
+ {
+ PrivilegedAction<Object> action = new PropertyAccessAction(PROPERTY_NAME);
+ return (String)AccessController.doPrivileged(action);
+ }
+
+
+ //--------------------------------- Utility privileged actions
+
+ private static class PropertyAccessAction implements PrivilegedAction<Object>
+ {
+ private String name;
+
+ PropertyAccessAction(String name)
+ {
+ this.name = name;
+ }
+
+ public Object run()
+ {
+ return System.getProperty(name);
+ }
+ }
+
+ private static class PropertyFileAccessAction implements
PrivilegedAction<Object>
+ {
+ private String filename;
+
+ PropertyFileAccessAction(String filename)
+ {
+ this.filename = filename;
+ }
+
+ public Object run()
+ {
+ InputStream inStream = null;
+ try
+ {
+ inStream = new FileInputStream(filename);
+ Properties props = new Properties();
+ props.load(inStream);
+ return props;
+ }
+ catch (IOException ex)
+ {
+ throw new SecurityException("Cannot load properties: " + filename,
ex);
+ }
+ finally
+ {
+ try
+ {
+ inStream.close();
+ }
+ catch (Exception e) {} //ignore
+ }
+ }
+ }
+
+ private static class PropertyFileExistAction implements
PrivilegedAction<Object>
+ {
+ private File file;
+
+ PropertyFileExistAction(File file)
+ {
+ this.file = file;
+ }
+
+ public Object run()
+ {
+ return file.exists();
+ }
+ }
+
+ /**
+ * A utility class for storing the document builder factory fields in the ThreadLocal
+ */
+ private static class DocumentBuilderFactoryFields {
+ private boolean coalescing = false;
+ private boolean expandEntityReferences = true;
+ private boolean ignoringComments = false;
+ private boolean ignoringElementContentWhitespace = false;
+ private boolean namespaceAware = false;
+ private Schema schema = null;
+ private boolean validating = false;
+ private boolean XIncludeAware = false;
+ private Map<String, Object> attributes = new HashMap<String,
Object>();
+ private Map<String, Boolean> features = new HashMap<String,
Boolean>();
+
+ public void copyTo(DocumentBuilderFactory target) throws
ParserConfigurationException
+ {
+ target.setCoalescing(coalescing);
+ target.setExpandEntityReferences(expandEntityReferences);
+ target.setIgnoringComments(ignoringComments);
+ target.setIgnoringElementContentWhitespace(ignoringElementContentWhitespace);
+ target.setNamespaceAware(namespaceAware);
+ target.setSchema(schema);
+ target.setValidating(validating);
+ target.setXIncludeAware(XIncludeAware);
+ for (String key : attributes.keySet())
+ {
+ target.setAttribute(key, attributes.get(key));
+ }
+ for (String key : features.keySet())
+ {
+ target.setFeature(key, features.get(key));
+ }
+ }
+
+ public Object getAttribute(String name)
+ {
+ return attributes.get(name);
+ }
+
+ public Boolean getFeature(String name)
+ {
+ return features.get(name);
+ }
+
+ public void setAttribute(String key, Object value)
+ {
+ this.attributes.put(key, value);
+ }
+
+ public void setFeature(String key, Boolean value)
+ {
+ this.features.put(key, value);
+ }
+
+ public boolean isCoalescing()
+ {
+ return coalescing;
+ }
+
+ public void setCoalescing(boolean coalescing)
+ {
+ this.coalescing = coalescing;
+ }
+
+ public boolean isExpandEntityReferences()
+ {
+ return expandEntityReferences;
+ }
+
+ public void setExpandEntityReferences(boolean expandEntityReferences)
+ {
+ this.expandEntityReferences = expandEntityReferences;
+ }
+
+ public boolean isIgnoringComments()
+ {
+ return ignoringComments;
+ }
+
+ public void setIgnoringComments(boolean ignoringComments)
+ {
+ this.ignoringComments = ignoringComments;
+ }
+
+ public boolean isIgnoringElementContentWhitespace()
+ {
+ return ignoringElementContentWhitespace;
+ }
+
+ public void setIgnoringElementContentWhitespace(boolean
ignoringElementContentWhitespace)
+ {
+ this.ignoringElementContentWhitespace = ignoringElementContentWhitespace;
+ }
+
+ public boolean isNamespaceAware()
+ {
+ return namespaceAware;
+ }
+
+ public void setNamespaceAware(boolean namespaceAware)
+ {
+ this.namespaceAware = namespaceAware;
+ }
+
+ public Schema getSchema()
+ {
+ return schema;
+ }
+
+ public void setSchema(Schema schema)
+ {
+ this.schema = schema;
+ }
+
+ public boolean isValidating()
+ {
+ return validating;
+ }
+
+ public void setValidating(boolean validating)
+ {
+ this.validating = validating;
+ }
+
+ public boolean isXIncludeAware()
+ {
+ return XIncludeAware;
+ }
+
+ public void setXIncludeAware(boolean includeAware)
+ {
+ XIncludeAware = includeAware;
+ }
+ }
+
+}
Added:
common/branches/jbossws-common-1.1.0/src/main/java/org/jboss/wsf/common/SecurityActions.java
===================================================================
---
common/branches/jbossws-common-1.1.0/src/main/java/org/jboss/wsf/common/SecurityActions.java
(rev 0)
+++
common/branches/jbossws-common-1.1.0/src/main/java/org/jboss/wsf/common/SecurityActions.java 2009-12-23
13:39:00 UTC (rev 11354)
@@ -0,0 +1,92 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.wsf.common;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+
+/**
+ * Security actions for this package
+ *
+ * @author alessio.soldano(a)jboss.com
+ * @since 19-Jun-2009
+ *
+ */
+class SecurityActions
+{
+ /**
+ * Get context classloader.
+ *
+ * @return the current context classloader
+ */
+ static ClassLoader getContextClassLoader()
+ {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm == null)
+ {
+ return Thread.currentThread().getContextClassLoader();
+ }
+ else
+ {
+ return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>()
{
+ public ClassLoader run()
+ {
+ return Thread.currentThread().getContextClassLoader();
+ }
+ });
+ }
+ }
+
+ /**
+ * Load a class using the provided classloader
+ *
+ * @param name
+ * @return
+ * @throws PrivilegedActionException
+ */
+ static Class<?> loadClass(final ClassLoader cl, final String name) throws
PrivilegedActionException, ClassNotFoundException
+ {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm == null)
+ {
+ return cl.loadClass(name);
+ }
+ else
+ {
+ return AccessController.doPrivileged(new
PrivilegedExceptionAction<Class<?>>() {
+ public Class<?> run() throws PrivilegedActionException
+ {
+ try
+ {
+ return cl.loadClass(name);
+ }
+ catch (Exception e)
+ {
+ throw new PrivilegedActionException(e);
+ }
+ }
+ });
+ }
+ }
+}
\ No newline at end of file
Property changes on:
common/branches/jbossws-common-1.1.0/src/main/java/org/jboss/wsf/common/SecurityActions.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Copied:
common/branches/jbossws-common-1.1.0/src/test/java/org/jboss/test/ws/common/utils/DummyDocumentBuilderFactory.java
(from rev 11350,
common/trunk/src/test/java/org/jboss/test/ws/common/utils/DummyDocumentBuilderFactory.java)
===================================================================
---
common/branches/jbossws-common-1.1.0/src/test/java/org/jboss/test/ws/common/utils/DummyDocumentBuilderFactory.java
(rev 0)
+++
common/branches/jbossws-common-1.1.0/src/test/java/org/jboss/test/ws/common/utils/DummyDocumentBuilderFactory.java 2009-12-23
13:39:00 UTC (rev 11354)
@@ -0,0 +1,64 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2009, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.test.ws.common.utils;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+/**
+ * A dummy document builder factory just for testing in {@link
JBossWSDocumentBuilderFactoryTestCase}.
+ *
+ * @author alessio.soldano(a)jboss.com
+ * @since 23-Dec-2009
+ *
+ */
+public class DummyDocumentBuilderFactory extends DocumentBuilderFactory
+{
+ @Override
+ public Object getAttribute(String name) throws IllegalArgumentException
+ {
+ return null;
+ }
+
+ @Override
+ public boolean getFeature(String name) throws ParserConfigurationException
+ {
+ return false;
+ }
+
+ @Override
+ public DocumentBuilder newDocumentBuilder() throws ParserConfigurationException
+ {
+ return null;
+ }
+
+ @Override
+ public void setAttribute(String name, Object value) throws IllegalArgumentException
+ {
+ }
+
+ @Override
+ public void setFeature(String name, boolean value) throws
ParserConfigurationException
+ {
+ }
+}
Copied:
common/branches/jbossws-common-1.1.0/src/test/java/org/jboss/test/ws/common/utils/JBossWSDocumentBuilderFactoryTestCase.java
(from rev 11350,
common/trunk/src/test/java/org/jboss/test/ws/common/utils/JBossWSDocumentBuilderFactoryTestCase.java)
===================================================================
---
common/branches/jbossws-common-1.1.0/src/test/java/org/jboss/test/ws/common/utils/JBossWSDocumentBuilderFactoryTestCase.java
(rev 0)
+++
common/branches/jbossws-common-1.1.0/src/test/java/org/jboss/test/ws/common/utils/JBossWSDocumentBuilderFactoryTestCase.java 2009-12-23
13:39:00 UTC (rev 11354)
@@ -0,0 +1,191 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2009, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.test.ws.common.utils;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import junit.framework.TestCase;
+
+import org.jboss.wsf.common.JBossWSDocumentBuilderFactory;
+
+/**
+ * Tests for the JBossWSDocumentBuilderFactory
+ *
+ * @author alessio.soldano(a)jboss.com
+ * @since 23-Dec-2009
+ *
+ */
+public class JBossWSDocumentBuilderFactoryTestCase extends TestCase
+{
+ public void testCaching() throws Exception
+ {
+ final String propName = "javax.xml.parsers.DocumentBuilderFactory";
+ String origValue = System.getProperty(propName);
+ try
+ {
+ //remove system prop if any, to get the same factory for a given classloader
+ System.getProperties().remove(propName);
+ DocumentBuilderFactory factory1 = JBossWSDocumentBuilderFactory.newInstance();
+ DocumentBuilderFactory factory2 = JBossWSDocumentBuilderFactory.newInstance();
+ assertTrue("Expected the same factory", factory1.equals(factory2));
+
+ //set the system prop, we should get different factories every time as the
classloader based cache is by-passed
+ System.setProperty(propName,
DummyDocumentBuilderFactory.class.getCanonicalName());
+ DocumentBuilderFactory factory3 = JBossWSDocumentBuilderFactory.newInstance();
+ assertTrue("Expected different factories",
!factory3.equals(factory1));
+ assertTrue("Expected different factories",
!factory3.equals(factory2));
+ DocumentBuilderFactory factory4 = JBossWSDocumentBuilderFactory.newInstance();
+ assertTrue("Expected different factories",
!factory4.equals(factory1));
+ assertTrue("Expected different factories",
!factory4.equals(factory2));
+ assertTrue("Expected different factories",
!factory4.equals(factory3));
+
+ //remove the prop again, we should get the first factory
+ System.getProperties().remove(propName);
+ DocumentBuilderFactory factory5 = JBossWSDocumentBuilderFactory.newInstance();
+ assertTrue("Expected the same factory", factory5.equals(factory1));
+
+ ClassLoader origLoader = Thread.currentThread().getContextClassLoader();
+ try
+ {
+ //change context classloader
+ Thread.currentThread().setContextClassLoader(new
TestClassLoader(origLoader));
+ DocumentBuilderFactory factory6 =
JBossWSDocumentBuilderFactory.newInstance();
+ assertTrue("Expected different factories",
!factory6.equals(factory1));
+ DocumentBuilderFactory factory7 =
JBossWSDocumentBuilderFactory.newInstance();
+ assertTrue("Expected the same factory",
factory7.equals(factory6));
+ }
+ finally
+ {
+ Thread.currentThread().setContextClassLoader(origLoader);
+ }
+ }
+ finally
+ {
+ if (origValue == null)
+ {
+ System.getProperties().remove(propName);
+ }
+ else
+ {
+ System.setProperty(propName, origValue);
+ }
+ }
+ }
+
+ public void testThreadSafety() throws Exception
+ {
+ DocumentBuilderFactory factory = JBossWSDocumentBuilderFactory.newInstance();
+ List<Callable<Boolean>> callables = new
LinkedList<Callable<Boolean>>();
+ for (int j = 0; j < 3; j++)
+ {
+ for (int i = 0; i < 10; i++)
+ {
+ callables.add(new TestCallable(factory, true, true, true));
+ }
+ for (int i = 0; i < 10; i++)
+ {
+ callables.add(new TestCallable(factory, true, true, false));
+ }
+ for (int i = 0; i < 10; i++)
+ {
+ callables.add(new TestCallable(factory, true, false, false));
+ }
+ for (int i = 0; i < 10; i++)
+ {
+ callables.add(new TestCallable(factory, false, false, false));
+ }
+ for (int i = 0; i < 10; i++)
+ {
+ callables.add(new TestCallable(factory, false, false, true));
+ }
+ for (int i = 0; i < 10; i++)
+ {
+ callables.add(new TestCallable(factory, false, true, true));
+ }
+ for (int i = 0; i < 10; i++)
+ {
+ callables.add(new TestCallable(factory, false, true, false));
+ }
+ }
+ ExecutorService es = Executors.newFixedThreadPool(210);
+ List<Future<Boolean>> futures = es.invokeAll(callables);
+ for (Future<Boolean> f : futures)
+ {
+ assertTrue(f.get());
+ }
+ }
+
+ /**
+ * A Callable that use the provided thread safe factory to create a document builder
and verifies it has the required configuration
+ */
+ public static class TestCallable implements Callable<Boolean>
+ {
+ private final DocumentBuilderFactory factory;
+ private final Boolean namespaceAware;
+ private final Boolean validating;
+ private final Boolean XIncludeAware;
+
+ public TestCallable(DocumentBuilderFactory factory, boolean namespaceAware, boolean
validating, boolean includeAware)
+ {
+ this.factory = factory;
+ this.namespaceAware = namespaceAware;
+ this.validating = validating;
+ this.XIncludeAware = includeAware;
+ }
+
+ public Boolean call() throws Exception
+ {
+ factory.setNamespaceAware(namespaceAware);
+ factory.setValidating(validating);
+ factory.setXIncludeAware(XIncludeAware);
+ DocumentBuilder builder = factory.newDocumentBuilder();
+ if (!namespaceAware.equals(builder.isNamespaceAware()))
+ return false;
+ if (!validating.equals(builder.isValidating()))
+ return false;
+ if (!XIncludeAware.equals(builder.isXIncludeAware()))
+ return false;
+ return true;
+ }
+
+ }
+
+ /**
+ * A ClassLoader doing nothing except falling back to its parent
+ */
+ public static class TestClassLoader extends ClassLoader
+ {
+ public TestClassLoader(ClassLoader parent)
+ {
+ super(parent);
+ }
+ }
+
+}