You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commons-dev@ws.apache.org by Jeff Barrett <ba...@us.ibm.com> on 2009/04/03 15:44:57 UTC

Re: svn commit: r757453 - in /webservices/commons/trunk/modules/XmlSchema/src: main/java/org/apache/ws/commons/schema/SchemaBuilder.java test/java/tests/SchemaBuilderCacheTest.java

Hi Daniel,

+1 to both those suggestions.

Thanks,
Jeff

IBM Software Group - WebSphere Web Services Development
Phone: (512) 286-5256 or TieLine: 363-5256
Internet e-mail and Sametime ID: barrettj@us.ibm.com



From:
Daniel Kulp <dk...@apache.org>
To:
commons-dev@ws.apache.org
Cc:
woodroy@apache.org
Date:
03/23/2009 12:25 PM
Subject:
Re: svn commit: r757453 - in 
/webservices/commons/trunk/modules/XmlSchema/src: 
main/java/org/apache/ws/commons/schema/SchemaBuilder.java 
test/java/tests/SchemaBuilderCacheTest.java




Couple questions about this commit:


1) Why not just use a ThreadLocal<Map<....>> instead of mimicking one with 
a 
HashMap based on thread id?   That way, if the Thread goes away before 
someone 
clears it, we know the the data will get garbage collected.

2) Collections.synchronizedMap(new Hashtable<String, 
SoftReference<XmlSchema>>())
Uh.....    Hashtable is already synchronized.    Should that be a HashMap?

Dan



On Mon March 23 2009 1:14:57 pm woodroy@apache.org wrote:
> Author: woodroy
> Date: Mon Mar 23 17:14:56 2009
> New Revision: 757453
>
> URL: http://svn.apache.org/viewvc?rev=757453&view=rev
> Log:
> Update initCache and clearCache to account for multi-threaded case.
> Essentially, one thread could change the static 'resolvedSchema' 
variable
> from null to non-null after another thread found the value to be null.
>
> Contributor: Jeff Barrett via Roy Wood
>
>
> Added:
> 
> 
webservices/commons/trunk/modules/XmlSchema/src/test/java/tests/SchemaBuild
>erCacheTest.java Modified:
> 
> 
webservices/commons/trunk/modules/XmlSchema/src/main/java/org/apache/ws/com
>mons/schema/SchemaBuilder.java
>
> Modified:
> 
webservices/commons/trunk/modules/XmlSchema/src/main/java/org/apache/ws/com
>mons/schema/SchemaBuilder.java URL:
> 
http://svn.apache.org/viewvc/webservices/commons/trunk/modules/XmlSchema/sr

>c/main/java/org/apache/ws/commons/schema/SchemaBuilder.java?rev=757453&r1=75
>7452&r2=757453&view=diff
> 
===========================================================================
>=== ---
> 
webservices/commons/trunk/modules/XmlSchema/src/main/java/org/apache/ws/com
>mons/schema/SchemaBuilder.java (original) +++
> 
webservices/commons/trunk/modules/XmlSchema/src/main/java/org/apache/ws/com
>mons/schema/SchemaBuilder.java Mon Mar 23 17:14:56 2009 @@ -24,6 +24,7 @@
>  import java.util.Collections;
>  import java.util.HashMap;
>  import java.util.HashSet;
> +import java.util.Hashtable;
>  import java.util.List;
>  import java.util.Map;
>  import java.util.Set;
> @@ -66,7 +67,8 @@
>       * initialize resolvedSchemas to non-null Clearing of cache is done 
by
> calling clearCache() which will * clear and nullify resolvedSchemas
>       */
> -    private static Map<String, SoftReference<XmlSchema>> 
resolvedSchemas;
> +
> +    private static Map<String, Map<String, SoftReference<XmlSchema>>>
> resolvedSchemas; private static final String[] RESERVED_ATTRIBUTES_LIST 
= 
> {"name", "type", "default", @@ -109,16 +111,108 @@
>          currentSchema = new XmlSchema();
>      }
>
> +
> +              /**
> +     * Setup the cache to be used by the current thread of execution.
> Multiple +     * threads can use the cache, and each one must call this
> method at +     * some point prior to attempting to resolve the first
> schema, or the cache +     * will not be used on that thread.
> +     *
> +     * IMPORTANT: The thread MUST call clearCache() when it is done 
with
> the +     * schemas or a large amount of memory may remain in-use.
> +     */
> +    public static synchronized void initCache() {
> +
> +              if (resolvedSchemas == null) {
> +            resolvedSchemas =
> +                      Collections.synchronizedMap(new HashMap<String, 
Map<String,
> SoftReference<XmlSchema>>>()); +        }
> +
> +        String threadID = 
String.valueOf(Thread.currentThread().getId());
> +
> +        Map<String, SoftReference<XmlSchema>> threadResolvedSchemas =
> +                      resolvedSchemas.get(threadID);
> +
> +        // If there is no entry yet for this thread ID, then create one
> +        if (threadResolvedSchemas == null) {
> +            threadResolvedSchemas = Collections.synchronizedMap(new
> Hashtable<String, SoftReference<XmlSchema>>()); + 
> resolvedSchemas.put(threadID, threadResolvedSchemas); +
> +        }
> +    }
> +
> +    /**
> +     * Remove any entries from the cache for the current thread. 
Entries
> for +     * other threads are not altered.
> +     */
>      public static synchronized void clearCache() {
>          if (resolvedSchemas != null) {
> -            resolvedSchemas.clear(); // necessary?
> -            resolvedSchemas = null;
> +            String threadID =
> String.valueOf(Thread.currentThread().getId()); +            // If there
> are entries for this thread ID, then clear them and +            // 
remove
> the entry
> +            Map<String, SoftReference<XmlSchema>> threadResolvedSchemas 
=
> +                      resolvedSchemas.get(threadID);
> +
> +            if (threadResolvedSchemas != null) {
> +                threadResolvedSchemas.clear();
> +                resolvedSchemas.remove(threadID);
> +            }
> +        }
> +    }
> +
> +    /**
> +     * Return a cached schema if one exists for this thread.  In order 
for
> schemas to be cached +     * the thread must have done an initCache()
> previously.
> +     * The parameters are used to construct a key used to lookup the
> schema +     * @param targetNamespace
> +     * @param schemaLocation
> +     * @param baseUri
> +     * @return The cached schema if one exists for this thread or null.
> +     */
> +    private XmlSchema getCachedSchema(String targetNamespace,
> +            String schemaLocation, String baseUri) {
> +
> +        XmlSchema resolvedSchema = null;
> +
> +        if (resolvedSchemas != null) {  // cache is initialized, use it
> +            String threadID =
> String.valueOf(Thread.currentThread().getId()); +            Map<String,
> SoftReference<XmlSchema>> threadResolvedSchemas = + 
>                resolvedSchemas.get(threadID);
> +            if (threadResolvedSchemas != null) {
> +                // Not being very smart about this at the moment. One
> could, for example, +                // see that the schemaLocation or
> baseUri is the same as another, but differs +                // only by 
a
> trailing slash. As it is now, we assume a single character difference +  
 
>            // means it's a schema that has yet to be resolved. +  
>     String schemaKey = targetNamespace + schemaLocation + baseUri; +  
>         SoftReference<XmlSchema> softref =
> threadResolvedSchemas.get(schemaKey); +                if (softref != 
null)
> {
> +                    resolvedSchema = softref.get();
> +                }
> +            }
>          }
> +        return resolvedSchema;
>      }
>
> -    public static synchronized void initCache() {
> -        if (resolvedSchemas == null) {
> -            resolvedSchemas = Collections.synchronizedMap(new
> HashMap<String, SoftReference<XmlSchema>>()); +    /**
> +     * Add an XmlSchema to the cache if the current thread has the 
cache
> enabled. +     * The first three parameters are used to construct a key
> +     * @param targetNamespace
> +     * @param schemaLocation
> +     * @param baseUri
> +     * This parameter is the value put under the key (if the cache is
> enabled) +     * @param readSchema
> +     */
> +    private void putCachedSchema(String targetNamespace, String
> schemaLocation, +            String baseUri, XmlSchema readSchema) {
> +
> +        if (resolvedSchemas != null) {
> +            String threadID =
> String.valueOf(Thread.currentThread().getId()); +            Map<String,
> SoftReference<XmlSchema>> threadResolvedSchemas = + 
>                resolvedSchemas.get(threadID);
> +            if (threadResolvedSchemas != null) {
> +                String schemaKey = targetNamespace + schemaLocation +
> baseUri; +                threadResolvedSchemas.put(schemaKey, new
> SoftReference<XmlSchema>(readSchema)); +            }
>          }
>      }
>
> @@ -917,22 +1011,10 @@
>      XmlSchema resolveXmlSchema(String targetNamespace, String
> schemaLocation, String baseUri, TargetNamespaceValidator validator) {
>
> -        String schemaKey = null;
> -        if (resolvedSchemas != null) { // cache is initialized, use it
> -            // Not being very smart about this at the moment. One 
could,
> for example, -            // see that the schemaLocation or baseUri is 
the
> same as another, but differs -            // only by a trailing slash. 
As
> it is now, we assume a single character difference -            // means
> it's a schema that has yet to be resolved.
> -            schemaKey = Thread.currentThread().getId() + 
targetNamespace +
> schemaLocation + baseUri; -            SoftReference<XmlSchema> softref 
=
> resolvedSchemas.get(schemaKey); -            if (softref != null) {
> -                XmlSchema resolvedSchema = softref.get();
> -                if (resolvedSchema != null) {
> -                    return resolvedSchema;
> -                }
> -            }
> +        if (getCachedSchema(targetNamespace, schemaLocation, baseUri) 
!=
> null) { +            return getCachedSchema(targetNamespace,
> schemaLocation, baseUri); }
> -
> +
>          // use the entity resolver provided if the schema location is
> present null if (schemaLocation != null && !"".equals(schemaLocation)) {
> InputSource source =
> collection.getSchemaResolver().resolveEntity(targetNamespace, @@ -958,9
> +1040,7 @@
>                  collection.push(key);
>                  try {
>                      XmlSchema readSchema = collection.read(source, 
null,
> validator); -                    if (resolvedSchemas != null) {
> -                        resolvedSchemas.put(schemaKey, new
> SoftReference<XmlSchema>(readSchema)); -                    }
> +                    putCachedSchema(targetNamespace, schemaLocation,
> baseUri, readSchema); return readSchema;
>                  } catch (Exception e) {
>                      throw new RuntimeException(e);
>
> Added:
> 
webservices/commons/trunk/modules/XmlSchema/src/test/java/tests/SchemaBuild
>erCacheTest.java URL:
> 
http://svn.apache.org/viewvc/webservices/commons/trunk/modules/XmlSchema/sr

>c/test/java/tests/SchemaBuilderCacheTest.java?rev=757453&view=auto
> 
===========================================================================
>=== ---
> 
webservices/commons/trunk/modules/XmlSchema/src/test/java/tests/SchemaBuild
>erCacheTest.java (added) +++
> 
webservices/commons/trunk/modules/XmlSchema/src/test/java/tests/SchemaBuild
>erCacheTest.java Mon Mar 23 17:14:56 2009 @@ -0,0 +1,424 @@
> +/*
> + * 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 tests;
> +
> +import java.lang.ref.SoftReference;
> +import java.lang.reflect.Field;
> +import java.util.Hashtable;
> +import java.util.Map;
> +
> +import javax.xml.parsers.DocumentBuilderFactory;
> +
> +import org.apache.ws.commons.schema.SchemaBuilder;
> +import org.apache.ws.commons.schema.XmlSchema;
> +import org.apache.ws.commons.schema.XmlSchemaCollection;
> +import org.w3c.dom.Document;
> +
> +import org.junit.Assert;
> +import org.junit.Test;
> +import org.junit.Ignore;
> +
> +/**
> + * Test the resolved Schema cache.
> + */
> +public class SchemaBuilderCacheTest extends Assert {
> +
> +              /**
> +     * Test that if the cache is not initialized, then it should not be
> used when a schema is read. +     * @throws Exception
> +     */
> +              @Test
> +    public void testResolveCacheUninitialized() throws Exception {
> +        Document doc = setupDocument();
> +        XmlSchemaCollection schemaCol = setupXmlSchemaCollection();
> +        XmlSchema schema = schemaCol.read(doc,null);
> +        assertNotNull(schema);
> +
> +        // If the cache is not in use, then it should be null
> +        assertNull(getResolvedSchemasHashtable());
> +    }
> +
> +    /**
> +     * Test if the cache is initialized it will be populated when a 
schema
> is read and it will +     * be cleared when the clearCache method is
> called.
> +     * @throws Exception
> +     */
> +              @Test
> +    public void testResolveCacheInitialized() throws Exception {
> +        try {
> +            SchemaBuilder.initCache();
> +            Document doc = setupDocument();
> +            XmlSchemaCollection schemaCol = setupXmlSchemaCollection();
> +            XmlSchema schema = schemaCol.read(doc, null);
> +            assertNotNull(schema);
> +
> +            // If the cache is in use, it should not be null and there
> should +            // be an entry for this thread ID
> +            assertNotNull(getResolvedSchemasHashtable());
> +            Map<String, SoftReference<XmlSchema>> threadHT =
> getThreadResolvedSchemaHashtable(); + assertNotNull(threadHT);
> +            assertFalse(threadHT.isEmpty());
> +            assertEquals(1, threadHT.size());
> +
> +            // After clearing the cache, there should be no entry for 
this
> thread ID, and +            // the hashtable should not be null
> +            SchemaBuilder.clearCache();
> +            assertNotNull(getResolvedSchemasHashtable());
> +            assertNull(getThreadResolvedSchemaHashtable());
> +            System.out.println("Line 13");
> +        } finally {
> +            resetResolvedSchemasHashtable();
> +        }
> +
> +        // If the cache is enabled, then it should be non-null
> +    }
> +
> +    /**
> +     * Test that threads can not affect the cache for other threads.
> +     */
> +              @Test
> +    public void testMultithreadCache() {
> +        try {
> +            MultithreadUpdateLockMonitor testMonitor = new
> MultithreadUpdateLockMonitor(); + 
> startupTestThreads(testMonitor);
> +
> +            if (testMonitor.t1Exception != null) {
> +                fail("Thread T1 encountred an error: "
> +                        + testMonitor.t1Exception.toString());
> +            }
> +            if (testMonitor.t2Exception != null) {
> +                fail("Thread T2 encountred an error: "
> +                        + testMonitor.t2Exception.toString());
> +            }
> +            if (testMonitor.t3Exception != null) {
> +                fail("Thread T3 encountred an error: "
> +                        + testMonitor.t3Exception.toString());
> +            }
> +        } finally {
> +            resetResolvedSchemasHashtable();
> +        }
> +    }
> +
> + 
> 
//=========================================================================
>===================== +    // Utility Methods
> + 
> 
//=========================================================================
>===================== +
> +    static Document setupDocument() {
> +        DocumentBuilderFactory documentBuilderFactory =
> DocumentBuilderFactory.newInstance(); + 
> documentBuilderFactory.setNamespaceAware(true);
> +        Document doc;
> +        try {
> +            doc = documentBuilderFactory.newDocumentBuilder().
> +                    parse(Resources.asURI("importBase.xsd"));
> +        } catch (Exception e) {
> +            throw new RuntimeException(e);
> +        }
> +        return doc;
> +    }
> +
> +    static XmlSchemaCollection setupXmlSchemaCollection() {
> +        XmlSchemaCollection schemaCol = new XmlSchemaCollection();
> +        schemaCol.setBaseUri(Resources.TEST_RESOURCES);
> +        return schemaCol;
> +    }
> +
> +    /**
> +     * Override the protection on the resolvedSchemas attribute in the
> SchemaBuilder class +     * and return its current value.
> +     * @return value of Hashtable resolvedSchemas, which may be null
> +     * @throws Exception If there are problems with Java reflection
> (should not happen) +     */
> +    static Map<String, Map<String, SoftReference<XmlSchema>>>
> getResolvedSchemasHashtable(){ +               Map<String, Map<String,
> SoftReference<XmlSchema>>> ht = null; +        try {
> +            Field field =
> SchemaBuilder.class.getDeclaredField("resolvedSchemas"); + 
> field.setAccessible(true);
> +            ht = (Map<String, Map<String,
> SoftReference<XmlSchema>>>)field.get(null); +        } catch (Exception 
e)
> {
> +            throw new RuntimeException(e);
> +        }
> +        return ht;
> +    }
> +
> +    /**
> +     * Return the Hashtable for the current thread or null if there is 
not
> one. +     * @return
> +     */
> +    static Map<String, SoftReference<XmlSchema>>
> getThreadResolvedSchemaHashtable() { +                 Map<String,
> SoftReference<XmlSchema>> threadHashtable = null; +        Map<String,
> Map<String, SoftReference<XmlSchema>>> ht = 
getResolvedSchemasHashtable();
> +        if (ht != null) {
> +            String threadID =
> String.valueOf(Thread.currentThread().getId()); + 
> threadHashtable = (Map<String, SoftReference<XmlSchema>>) 
ht.get(threadID);
> +        }
> +        return threadHashtable;
> +    }
> +
> +    /**
> +     * Set the resolvedSchemas collection to null.  This should be done 
in
> a finally block +     * of any tests that cause an 
SchemaBuilder.initCache
> to be done to cleanup for the next +     * test.
> +     */
> +    static void resetResolvedSchemasHashtable() {
> +        try {
> +            Field field =
> SchemaBuilder.class.getDeclaredField("resolvedSchemas"); + 
> field.setAccessible(true);
> +            Map<String, Map<String, SoftReference<XmlSchema>>> ht =
> (Map<String, Map<String, SoftReference<XmlSchema>>>) field.get(null); +  
 
>        if (ht != null) {
> +                ht.clear();
> +                field.set(null, null);
> +            }
> +        }catch (Exception e) {
> +            throw new RuntimeException(e);
> +        }
> +    }
> +
> +    // Amount of time the testcase should wait on the test threads 
before
> timing out +    private static int THREAD_TIMEOUT = 90000;
> +
> +    /**
> +     * Configure and start the test threads for the multi-threaded
> testing.  The threads will +     * perform various tests between 
themselves
> such as clearing cache in one thread and making +     * sure the cache 
used
> by a different thread is not affected.  A monitor is used to control +  
> * the synchonization between the threads and for communicating faliures
> back to the test +     * method.
> +     *
> +     * See thed Runnable classes for details on the tests performed.
> +     *
> +     * @param testMonitor Used to synchronize the tests between the
> threads +     */
> +    private void startupTestThreads(MultithreadUpdateLockMonitor
> testMonitor) { +        TestingRunnable1 testRunnable1 = new
> TestingRunnable1();
> +        testRunnable1.testMonitor = testMonitor;
> +
> +        TestingRunnable2 testRunnable2 = new TestingRunnable2();
> +        testRunnable2.testMonitor = testMonitor;
> +
> +        TestingRunnable3 testRunnable3 = new TestingRunnable3();
> +        testRunnable3.testMonitor = testMonitor;
> +
> +        Thread thread1 = new Thread(testRunnable1);
> +        Thread thread2 = new Thread(testRunnable2);
> +        Thread thread3 = new Thread(testRunnable3);
> +
> +        thread1.start();
> +        thread2.start();
> +        thread3.start();
> +
> +        // Join the threads to wait for their completion, specifying a
> timeout to prevent +        // a testcase hang if something goes wrong 
with
> the threads. +        try {
> +            thread1.join(THREAD_TIMEOUT);
> +            thread2.join(THREAD_TIMEOUT);
> +            thread3.join(THREAD_TIMEOUT);
> +        } catch (InterruptedException e) {
> +            e.printStackTrace();
> +            fail("Unable to join to testing threads");
> +        }
> +    }
> +}
> +
> +/**
> + * Monitor used to control synchronization between the testing threads 
and
> communicate failures + * back to the test method.
> + */
> +class MultithreadUpdateLockMonitor {
> +    boolean t1SetupComplete = false;
> +    boolean t2SetupComplete = false;
> +    Exception t1Exception = null;
> +    Exception t2Exception = null;
> +    Exception t3Exception = null;
> +}
> 
+//========================================================================
>========================= +// Test execution threads
> 
+//========================================================================
>========================= +/**
> + * Thread 1 will do the following
> + * - Initialize the cache and verify it was used during a read
> + * - Unblock Thread 2
> + * - Wait until Thread 2 unblocks it
> + * - Verify that the clearCache done by Thread 2 did not affect this
> Thread's cache. + */
> +@Ignore
> +class TestingRunnable1 implements Runnable {
> +
> +    MultithreadUpdateLockMonitor testMonitor = null;
> +
> +    public void run() {
> +        SchemaBuilder.initCache();
> +        Document doc = SchemaBuilderCacheTest.setupDocument();
> +        XmlSchemaCollection schemaCol =
> SchemaBuilderCacheTest.setupXmlSchemaCollection(); +        XmlSchema
> schema = schemaCol.read(doc, null);
> +        if (schema == null) {
> +            testMonitor.t1Exception = new Exception("Schema was null");
> +        }
> +
> +        // If the cache is in use, it should not be null and there 
should
> be an entry for this +        // Thread
> +        if (SchemaBuilderCacheTest.getResolvedSchemasHashtable() == 
null)
> { +            testMonitor.t1Exception = new Exception("resolvedSchemas 
was
> null"); +        }
> +        Map<String, SoftReference<XmlSchema>> threadHT =
> SchemaBuilderCacheTest.getThreadResolvedSchemaHashtable(); +        if
> (threadHT == null ) {
> +            testMonitor.t1Exception = new Exception("Thread
> resolvedSchemas was null"); +        }
> +
> +        if (threadHT.isEmpty()) {
> +            testMonitor.t1Exception = new Exception("Thread
> resolvedSchemas was empty"); +        }
> +
> +        synchronized(testMonitor) {
> +            testMonitor.t1SetupComplete = true;
> +            testMonitor.notifyAll();
> +            while (!testMonitor.t2SetupComplete) {
> +                try {
> +                    testMonitor.wait();
> +                } catch (InterruptedException e) {
> +                    e.printStackTrace();
> +                    testMonitor.t1Exception = new RuntimeException(e);
> +                    throw (RuntimeException) testMonitor.t1Exception;
> +                }
> +            }
> +        }
> +
> +        // After the other thread does a reset, the cache for this 
thread
> should NOT be null +        if
> (SchemaBuilderCacheTest.getResolvedSchemasHashtable() == null) { +  
>   testMonitor.t1Exception = new Exception("resolvedSchemas was null 
after
> reset"); +        }
> +        threadHT =
> SchemaBuilderCacheTest.getThreadResolvedSchemaHashtable(); +        if
> (threadHT == null ) {
> +            testMonitor.t1Exception = new Exception("Thread
> resolvedSchemas was null after clear"); +        }
> +
> +        if (threadHT.isEmpty()) {
> +            testMonitor.t1Exception = new Exception("Thread
> resolvedSchemas was empty after clear"); +        }
> +
> +        // Issue our a clear on this TID, and now there should be no
> entires for it +        SchemaBuilder.clearCache();
> +        if (SchemaBuilderCacheTest.getResolvedSchemasHashtable() == 
null)
> { +            testMonitor.t1Exception = new Exception("resolvedSchemas 
was
> null after clear on TID"); +        }
> +
> +        threadHT =
> SchemaBuilderCacheTest.getThreadResolvedSchemaHashtable(); +        if
> (threadHT != null ) {
> +            testMonitor.t1Exception = new Exception("Thread
> resolvedSchemas was not null after clear"); +        }
> +    }
> +}
> +
> +/**
> + * Thread 2 will:
> + * - Block until released by Thread 1
> + * - Initialize the cache then make sure it was used for a resolve on 
this
> thread + * - clear the cache and make sure the entries for this Thread a
> removed + * - Unblock Thread 1 to make sure the clear did not affect it
> + */
> +@Ignore
> +class TestingRunnable2 implements Runnable {
> +
> +    MultithreadUpdateLockMonitor testMonitor = null;
> +
> +    public void run() {
> +        synchronized (testMonitor) {
> +            while (!testMonitor.t1SetupComplete) {
> +                try {
> +                    testMonitor.wait();
> +                } catch (InterruptedException e) {
> +                    e.printStackTrace();
> +                    testMonitor.t2Exception = new RuntimeException(e);
> +                    throw (RuntimeException) testMonitor.t2Exception;
> +                }
> +            }
> +        }
> +        SchemaBuilder.initCache();
> +        Document doc = SchemaBuilderCacheTest.setupDocument();
> +        XmlSchemaCollection schemaCol =
> SchemaBuilderCacheTest.setupXmlSchemaCollection(); +        XmlSchema
> schema = schemaCol.read(doc, null);
> +        if (schema == null) {
> +            testMonitor.t2Exception = new Exception("Schema was null");
> +        }
> +
> +        // If the cache is in use, it should not be null.
> +        if (SchemaBuilderCacheTest.getResolvedSchemasHashtable() == 
null)
> { +            testMonitor.t2Exception = new Exception("resolvedSchemas 
was
> null"); +        }
> +        Map<String, SoftReference<XmlSchema>> threadHT =
> SchemaBuilderCacheTest.getThreadResolvedSchemaHashtable(); +        if
> (threadHT == null ) {
> +            testMonitor.t2Exception = new Exception("Thread
> resolvedSchemas was null"); +        }
> +        if (threadHT.isEmpty()) {
> +            testMonitor.t2Exception = new Exception("Thread
> resolvedSchemas was empty"); +        }
> +
> +        // Issue our a clear on this TID, and now there should be no
> entires for it +        SchemaBuilder.clearCache();
> +        if (SchemaBuilderCacheTest.getResolvedSchemasHashtable() == 
null)
> { +            testMonitor.t2Exception = new Exception("resolvedSchemas 
was
> null after clear on TID"); +        }
> +
> +        threadHT =
> SchemaBuilderCacheTest.getThreadResolvedSchemaHashtable(); +        if
> (threadHT != null ) {
> +            testMonitor.t2Exception = new Exception("Thread
> resolvedSchemas was not null after clear"); +        }
> +
> +        synchronized (testMonitor) {
> +            testMonitor.t2SetupComplete = true;
> +            testMonitor.notifyAll();
> +        }
> +    }
> +}
> +
> +/**
> + * Test that the init done by Thread 1 does NOT cause the cache to be 
used
> by this thread + * which did not do an init.
> + */
> +@Ignore
> +class TestingRunnable3 implements Runnable {
> +    MultithreadUpdateLockMonitor testMonitor = null;
> +
> +    public void run() {
> +        synchronized (testMonitor) {
> +            while (!testMonitor.t1SetupComplete) {
> +                try {
> +                    testMonitor.wait();
> +                } catch (InterruptedException e) {
> +                    e.printStackTrace();
> +                    testMonitor.t3Exception = new RuntimeException(e);
> +                    throw (RuntimeException) testMonitor.t2Exception;
> +                }
> +            }
> +        }
> +        Document doc = SchemaBuilderCacheTest.setupDocument();
> +        XmlSchemaCollection schemaCol =
> SchemaBuilderCacheTest.setupXmlSchemaCollection(); +        XmlSchema
> schema = schemaCol.read(doc, null);
> +        if (schema == null) {
> +            testMonitor.t3Exception = new Exception("Schema was null");
> +        }
> +        Map<String, SoftReference<XmlSchema>> threadHT =
> SchemaBuilderCacheTest.getThreadResolvedSchemaHashtable(); +        if
> (threadHT != null ) {
> +            testMonitor.t3Exception = new Exception("Thread
> resolvedSchemas was not null"); +        }
> +
> +
> +    }
> +
> +}

-- 
Daniel Kulp
dkulp@apache.org
http://www.dankulp.com/blog