Repository: brooklyn-server Updated Branches: refs/heads/master b8211ed17 -> 43a8816c8
XML SpecResolver: use context instead of thread-local Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/3a2c4cb5 Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/3a2c4cb5 Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/3a2c4cb5 Branch: refs/heads/master Commit: 3a2c4cb580ecf368740fe8c52b9aa37339f9cd0e Parents: b8211ed Author: Aled Sage <[email protected]> Authored: Tue Mar 22 18:36:54 2016 +0000 Committer: Aled Sage <[email protected]> Committed: Tue Mar 22 18:57:37 2016 +0000 ---------------------------------------------------------------------- .../internal/AbstractBrooklynObjectSpec.java | 2 ++ .../core/mgmt/persist/XmlMementoSerializer.java | 18 +++++----- .../mgmt/persist/XmlMementoSerializerTest.java | 37 +++++++++++++++++++- 3 files changed, 46 insertions(+), 11 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/3a2c4cb5/api/src/main/java/org/apache/brooklyn/api/internal/AbstractBrooklynObjectSpec.java ---------------------------------------------------------------------- diff --git a/api/src/main/java/org/apache/brooklyn/api/internal/AbstractBrooklynObjectSpec.java b/api/src/main/java/org/apache/brooklyn/api/internal/AbstractBrooklynObjectSpec.java index 3f95342..ef99a27 100644 --- a/api/src/main/java/org/apache/brooklyn/api/internal/AbstractBrooklynObjectSpec.java +++ b/api/src/main/java/org/apache/brooklyn/api/internal/AbstractBrooklynObjectSpec.java @@ -235,6 +235,8 @@ public abstract class AbstractBrooklynObjectSpec<T,SpecT extends AbstractBrookly if (!Objects.equal(getCatalogItemId(), other.getCatalogItemId())) return false; if (!Objects.equal(getType(), other.getType())) return false; if (!Objects.equal(getTags(), other.getTags())) return false; + if (!Objects.equal(getConfig(), other.getConfig())) return false; + if (!Objects.equal(getFlags(), other.getFlags())) return false; if (!Objects.equal(getParameters(), other.getParameters())) return false; return true; } http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/3a2c4cb5/core/src/main/java/org/apache/brooklyn/core/mgmt/persist/XmlMementoSerializer.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/persist/XmlMementoSerializer.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/persist/XmlMementoSerializer.java index 0df2340..00693d9 100644 --- a/core/src/main/java/org/apache/brooklyn/core/mgmt/persist/XmlMementoSerializer.java +++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/persist/XmlMementoSerializer.java @@ -393,12 +393,6 @@ public class XmlMementoSerializer<T> extends XmlSerializer<T> implements Memento } } - // Would prefer this as a field of SpecConverter, but can't do that because the class is not static. - // Must use thread-local storage because a single instance of the SpecConverter is used by this - // XmlMementoSerializer. In BrooklynMementoPersisterToObjectStore.visitMemento, it uses a thread-pool - // for concurrently deserializing multiple objects. - private static final ThreadLocal<Object> SpecConverterLocalInstance = new ThreadLocal<Object>(); - /** When reading/writing specs, it checks whether there is a catalog item id set and uses it to load */ public class SpecConverter extends ReflectionConverter { SpecConverter() { @@ -460,7 +454,7 @@ public class XmlMementoSerializer<T> extends XmlSerializer<T> implements Memento result.catalogItemId(catalogItemId); return result; } finally { - SpecConverterLocalInstance.remove(); + context.put("SpecConverter.instance", null); if (customLoaderSet) { popXstreamCustomClassLoader(); } @@ -470,15 +464,19 @@ public class XmlMementoSerializer<T> extends XmlSerializer<T> implements Memento @Override protected Object instantiateNewInstance(HierarchicalStreamReader reader, UnmarshallingContext context) { // the super calls getAttribute which requires that we have not yet done moveDown, - // so we do this earlier and cache it for when we call super.unmarshal - Object instance = SpecConverterLocalInstance.get(); + // so we do this earlier and cache it for when we call super.unmarshal. + // Store this in the UnmarshallingContext. Note that we *must not* use a field of SpecConverter, + // because that same instance is used by everything calling XmlMementoSerializer (including multiple + // threads). + Object instance = context.get("SpecConverter.instance"); if (instance==null) throw new IllegalStateException("Instance should be created and cached"); return instance; } + protected void instantiateNewInstanceSettingCache(HierarchicalStreamReader reader, UnmarshallingContext context) { Object instance = super.instantiateNewInstance(reader, context); - SpecConverterLocalInstance.set(instance); + context.put("SpecConverter.instance", instance); } } http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/3a2c4cb5/core/src/test/java/org/apache/brooklyn/core/mgmt/persist/XmlMementoSerializerTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/brooklyn/core/mgmt/persist/XmlMementoSerializerTest.java b/core/src/test/java/org/apache/brooklyn/core/mgmt/persist/XmlMementoSerializerTest.java index 662e78d..aefd623 100644 --- a/core/src/test/java/org/apache/brooklyn/core/mgmt/persist/XmlMementoSerializerTest.java +++ b/core/src/test/java/org/apache/brooklyn/core/mgmt/persist/XmlMementoSerializerTest.java @@ -30,6 +30,8 @@ import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; +import java.util.concurrent.Callable; +import java.util.concurrent.Executors; import org.apache.brooklyn.api.catalog.CatalogItem; import org.apache.brooklyn.api.entity.Entity; @@ -70,7 +72,12 @@ import com.google.common.base.Objects; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; import com.google.common.collect.Maps; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; public class XmlMementoSerializerTest { @@ -300,6 +307,34 @@ public class XmlMementoSerializerTest { } @Test + public void testEntitySpecNested() throws Exception { + EntitySpec<?> obj = EntitySpec.create(TestEntity.class) + .configure("nest1", EntitySpec.create(TestEntity.class) + .configure("nest2", EntitySpec.create(TestEntity.class))); + assertSerializeAndDeserialize(obj); + } + + @Test + public void testEntitySpecManyConcurrently() throws Exception { + ListeningExecutorService executor = MoreExecutors.listeningDecorator(Executors.newCachedThreadPool()); + List<ListenableFuture<Void>> futures = Lists.newArrayList(); + try { + for (int i = 0; i < 100; i++) { + futures.add(executor.submit(new Callable<Void>() { + @Override public Void call() throws Exception { + EntitySpec<?> obj = EntitySpec.create(TestEntity.class); + assertSerializeAndDeserialize(obj); + return null; + }})); + } + Futures.allAsList(futures).get(); + + } finally { + executor.shutdownNow(); + } + } + + @Test public void testEntitySpecFromOsgi() throws Exception { TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_V1_PATH); ManagementContext mgmt = LocalManagementContextForTests.builder(true).disableOsgi(false).build(); @@ -612,4 +647,4 @@ public class XmlMementoSerializerTest { return Objects.hashCode(myStaticInnerField); } } -} +} \ No newline at end of file
