From 91a10a8ca1c4ef14c958af2d31e880c2e71ea2b8 Mon Sep 17 00:00:00 2001 From: John Blum Date: Wed, 10 Jun 2020 12:39:21 -0700 Subject: [PATCH] Move PdxInstance identifier/identity resolution logic from JsonCacheDataImporterExporter to PdxInstanceWrapper. --- .../geode/pdx/PdxInstanceWrapper.java | 128 ++++++- .../pdx/PdxInstanceWrapperUnitTests.java | 313 ++++++++++++++++ .../json/JsonCacheDataImporterExporter.java | 145 ++----- ...sonCacheDataImporterExporterUnitTests.java | 354 +----------------- 4 files changed, 475 insertions(+), 465 deletions(-) diff --git a/apache-geode-extensions/src/main/java/org/springframework/geode/pdx/PdxInstanceWrapper.java b/apache-geode-extensions/src/main/java/org/springframework/geode/pdx/PdxInstanceWrapper.java index 1235ad33..e41b5ca2 100644 --- a/apache-geode-extensions/src/main/java/org/springframework/geode/pdx/PdxInstanceWrapper.java +++ b/apache-geode-extensions/src/main/java/org/springframework/geode/pdx/PdxInstanceWrapper.java @@ -51,6 +51,7 @@ public class PdxInstanceWrapper implements PdxInstance, Sendable { public static final String AT_TYPE_FIELD_NAME = "@type"; public static final String CLASS_NAME_PROPERTY = "className"; public static final String ID_FIELD_NAME = "id"; + protected static final String NO_FIELD_NAME = ""; protected static final String ARRAY_BEGIN = "["; protected static final String ARRAY_END = "]"; @@ -202,14 +203,6 @@ public class PdxInstanceWrapper implements PdxInstance, Sendable { return getDelegate().isEnum(); } - /** - * @inheritDoc - */ - @Override - public boolean isIdentityField(String fieldName) { - return getDelegate().isIdentityField(fieldName); - } - /** * @inheritDoc */ @@ -226,10 +219,123 @@ public class PdxInstanceWrapper implements PdxInstance, Sendable { return getDelegate().getFieldNames(); } + /** + * Determines the {@link Object identifier} for, or {@link PdxInstance#isIdentityField(String) identity} of, + * this {@link PdxInstance}. + * + * @return the {@link Object identifier} for this {@link PdxInstance}; never {@literal null}. + * @throws IllegalStateException if the {@link PdxInstance} does not have an id. + * @see #isIdentityField(String) + * @see #getField(String) + * @see #getFieldNames() + * @see #getId() + */ + public Object getIdentifier() { + + Optional identityFieldName = nullSafeList(getFieldNames()).stream() + .filter(this::hasText) + .filter(this::isIdentityField) + .findFirst(); + + return identityFieldName + .map(this::getField) + .orElseGet(this::getId); + } + + /** + * Searches for a PDX {@link String field name} called {@literal id} on this {@link PdxInstance} + * and returns its {@link Object value} as the {@link Object identifier} for, + * or {@link PdxInstance#isIdentityField(String) identity} of, this {@link PdxInstance}. + * + * @return the {@link Object value} of the {@literal id} {@link String field} on this {@link PdxInstance}. + * @throws IllegalStateException if this {@link PdxInstance} does not have an id. + * @see #getAtIdentifier() + * @see #getField(String) + * @see #hasField(String) + */ + protected Object getId() { + + return hasField(ID_FIELD_NAME) + ? getField(ID_FIELD_NAME) + : getAtIdentifier(); + } + + /** + * Searches for a PDX {@link String field} declared by the {@literal @identifier} metadata {@link String field} + * on this {@link PdxInstance} and returns the {@link Object value} of this {@link String field} + * as the {@link Object identifier} for, or {@link PdxInstance#isIdentityField(String) identity} of, + * this {@link PdxInstance}. + * + * @return the {@link Object value} of the {@link String field} declared in the {@literal @identifier} metadata + * {@link String field} on this {@link PdxInstance}. + * @throws IllegalStateException if the {@link PdxInstance} does not have an id. + * @see org.apache.geode.pdx.PdxInstance + */ + protected Object getAtIdentifier() { + + return Optional.of(AT_IDENTIFIER_FIELD_NAME) + .filter(this::hasField) + .map(this::getField) + .map(String::valueOf) + .filter(this::hasField) + .map(this::getField) + .orElseThrow(() -> new IllegalStateException(String.format("PdxInstance for type [%1$s] has no %2$s", + getClassName(), resolveMessageForIdentifierError(this)))); + } + + private String resolveMessageForIdentifierError(PdxInstance pdxInstance) { + + String message = "declared identifier"; + + if (pdxInstance.hasField(ID_FIELD_NAME)) { + message = "id"; + } + else if (pdxInstance.hasField(AT_IDENTIFIER_FIELD_NAME)) { + + Object atIdentifierFieldValue = pdxInstance.getField(AT_IDENTIFIER_FIELD_NAME); + + String resolvedIdentifierFieldName = Objects.nonNull(atIdentifierFieldValue) + ? atIdentifierFieldValue.toString().trim() + : NO_FIELD_NAME; + + boolean identifierFieldNameWasDeclaredAndIsValid = pdxInstance.hasField(resolvedIdentifierFieldName); + + Object identifier = identifierFieldNameWasDeclaredAndIsValid + ? pdxInstance.getField(resolvedIdentifierFieldName) + : null; + + String ifMessage = "value [%s] for field [%s] declared in [%s]"; + String elseMessage = "field [%s] declared in [%s]"; + + message = identifierFieldNameWasDeclaredAndIsValid + ? String.format(ifMessage, identifier, resolvedIdentifierFieldName, AT_IDENTIFIER_FIELD_NAME) + : String.format(elseMessage, resolvedIdentifierFieldName, AT_IDENTIFIER_FIELD_NAME); + } + + return message; + } + /** * @inheritDoc */ @Override + public boolean isIdentityField(String fieldName) { + return getDelegate().isIdentityField(fieldName); + } + + /** + * Materializes an {@link Object} from the PDX bytes described by this {@link PdxInstance}. + * + * If these PDX bytes describe an {@link Object} parsed from JSON, then the JSON is reconstructed from + * this {@link PdxInstance} and mapped to an instance of the {@link Class type} identified by + * the {@literal @type} metadata PDX {@link String field} using Jackson's {@link ObjectMapper}. + * + * @return an {@link Object} constructed from the PDX bytes described by this {@link PdxInstance}. + * @see com.fasterxml.jackson.databind.ObjectMapper + * @see java.lang.Object + * @see #getObjectMapper() + */ + @Override public Object getObject() { return getObjectMapper() @@ -247,7 +353,7 @@ public class PdxInstanceWrapper implements PdxInstance, Sendable { return objectMapper.readValue(json, type); } catch (Throwable ignore) { - // TODO Add log statement + // TODO Log Throwable? return null; } }) @@ -370,6 +476,10 @@ public class PdxInstanceWrapper implements PdxInstance, Sendable { return String.format(FIELD_TYPE_VALUE, fieldName, nullSafeType(fieldValue), fieldValue); } + private boolean hasText(String value) { + return value != null && !value.trim().isEmpty(); + } + private boolean isArray(Object value) { return Objects.nonNull(value) && value.getClass().isArray(); } diff --git a/apache-geode-extensions/src/test/java/org/springframework/geode/pdx/PdxInstanceWrapperUnitTests.java b/apache-geode-extensions/src/test/java/org/springframework/geode/pdx/PdxInstanceWrapperUnitTests.java index 444655ce..57d387df 100644 --- a/apache-geode-extensions/src/test/java/org/springframework/geode/pdx/PdxInstanceWrapperUnitTests.java +++ b/apache-geode-extensions/src/test/java/org/springframework/geode/pdx/PdxInstanceWrapperUnitTests.java @@ -20,6 +20,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; @@ -34,6 +35,7 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; import java.io.DataOutput; import java.io.IOException; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Optional; @@ -264,6 +266,315 @@ public class PdxInstanceWrapperUnitTests { verifyNoMoreInteractions(mockPdxInstance); } + @Test + public void getIdentifierFromPdxInstanceHavingAnIdentity() { + + PdxInstance mockPdxInstance = mock(PdxInstance.class); + + doReturn(Arrays.asList("age", "id", "name")).when(mockPdxInstance).getFieldNames(); + doReturn(true).when(mockPdxInstance).isIdentityField(eq("id")); + doReturn(42).when(mockPdxInstance).getField(eq("id")); + + PdxInstanceWrapper wrapper = spy(new PdxInstanceWrapper(mockPdxInstance)); + + assertThat(wrapper.getIdentifier()).isEqualTo(42); + + verify(wrapper, never()).getId(); + verify(mockPdxInstance, times(1)).getFieldNames(); + verify(mockPdxInstance, times(1)).isIdentityField(eq("age")); + verify(mockPdxInstance, times(1)).isIdentityField(eq("id")); + verify(mockPdxInstance, never()).isIdentityField(eq("name")); + verify(mockPdxInstance, times(1)).getField(eq("id")); + verifyNoMoreInteractions(mockPdxInstance); + } + + @Test + public void getIdentifierFromPdxInstanceHavingNoFields() { + + PdxInstance mockPdxInstance = mock(PdxInstance.class); + + PdxInstanceWrapper wrapper = spy(new PdxInstanceWrapper(mockPdxInstance)); + + doReturn(null).when(mockPdxInstance).getFieldNames(); + doReturn(69).when(wrapper).getId(); + + assertThat(wrapper.getIdentifier()).isEqualTo(69); + + verify(wrapper, times(1)).getId(); + verify(mockPdxInstance, times(1)).getFieldNames(); + verify(mockPdxInstance, never()).isIdentityField(anyString()); + verify(mockPdxInstance, never()).getField(anyString()); + verifyNoMoreInteractions(mockPdxInstance); + } + + @Test + public void getIdentifierFromPdxInstanceHavingNoIdentityFields() { + + PdxInstance mockPdxInstance = mock(PdxInstance.class); + + PdxInstanceWrapper wrapper = spy(new PdxInstanceWrapper(mockPdxInstance)); + + doReturn(Arrays.asList("", "age", null, "name", " ")).when(mockPdxInstance).getFieldNames(); + doReturn(false).when(mockPdxInstance).isIdentityField(any()); + doReturn(99).when(wrapper).getId(); + + assertThat(wrapper.getIdentifier()).isEqualTo(99); + + verify(wrapper, times(1)).getId(); + verify(mockPdxInstance, times(1)).getFieldNames(); + verify(mockPdxInstance, times(1)).isIdentityField(eq("age")); + verify(mockPdxInstance, times(1)).isIdentityField(eq("name")); + verify(mockPdxInstance, never()).isIdentityField(isNull()); + verify(mockPdxInstance, never()).isIdentityField(eq("")); + verify(mockPdxInstance, never()).isIdentityField(eq(" ")); + verify(mockPdxInstance, never()).getField(anyString()); + verifyNoMoreInteractions(mockPdxInstance); + } + + @Test(expected = IllegalStateException.class) + public void getIdentifierFromPdxInstanceWithNoIdentifier() { + + PdxInstance mockPdxInstance = mock(PdxInstance.class); + + PdxInstanceWrapper wrapper = spy(new PdxInstanceWrapper(mockPdxInstance)); + + doReturn(Collections.singletonList("name")).when(mockPdxInstance).getFieldNames(); + doReturn(false).when(mockPdxInstance).isIdentityField(anyString()); + doThrow(new IllegalStateException("NO ID")).when(wrapper).getId(); + + try { + wrapper.getIdentifier(); + } + catch (IllegalStateException expected) { + + assertThat(expected).hasMessage("NO ID"); + assertThat(expected).hasNoCause(); + + throw expected; + } + finally { + verify(mockPdxInstance, times(1)).getFieldNames(); + verify(mockPdxInstance, times(1)).isIdentityField(eq("name")); + verify(mockPdxInstance, never()).getField(anyString()); + verify(wrapper, times(1)).getId(); + verifyNoMoreInteractions(mockPdxInstance); + } + } + + @Test + public void getIdFromPdxInstanceHavingIdField() { + + PdxInstance mockPdxInstance = mock(PdxInstance.class); + + PdxInstanceWrapper wrapper = spy(new PdxInstanceWrapper(mockPdxInstance)); + + doReturn(true).when(mockPdxInstance).hasField(eq(PdxInstanceWrapper.ID_FIELD_NAME)); + doReturn(42).when(mockPdxInstance).getField(eq(PdxInstanceWrapper.ID_FIELD_NAME)); + + assertThat(wrapper.getId()).isEqualTo(42); + + verify(mockPdxInstance, times(1)).hasField(eq(PdxInstanceWrapper.ID_FIELD_NAME)); + verify(mockPdxInstance, times(1)).getField(eq(PdxInstanceWrapper.ID_FIELD_NAME)); + verify(wrapper, never()).getAtIdentifier(); + verifyNoMoreInteractions(mockPdxInstance); + } + + @Test + public void getIdFromPdxInstanceHavingIdFieldWithNoValueReturnsNull() { + + PdxInstance mockPdxInstance = mock(PdxInstance.class); + + PdxInstanceWrapper wrapper = spy(new PdxInstanceWrapper(mockPdxInstance)); + + doReturn(true).when(mockPdxInstance).hasField(eq(PdxInstanceWrapper.ID_FIELD_NAME)); + doReturn(null).when(mockPdxInstance).getField(eq(PdxInstanceWrapper.ID_FIELD_NAME)); + + assertThat(wrapper.getId()).isNull(); + + verify(mockPdxInstance, times(1)).hasField(eq(PdxInstanceWrapper.ID_FIELD_NAME)); + verify(mockPdxInstance, times(1)).getField(eq(PdxInstanceWrapper.ID_FIELD_NAME)); + verify(wrapper, never()).getAtIdentifier(); + verifyNoMoreInteractions(mockPdxInstance); + } + + @Test + public void getIdFromPdxInstanceWithNoIdFieldCallsGetAtIdentifier() { + + PdxInstance mockPdxInstance = mock(PdxInstance.class); + + PdxInstanceWrapper wrapper = spy(new PdxInstanceWrapper(mockPdxInstance)); + + doReturn(false).when(mockPdxInstance).hasField(any()); + doReturn(99).when(wrapper).getAtIdentifier(); + + assertThat(wrapper.getId()).isEqualTo(99); + + verify(mockPdxInstance, times(1)).hasField(eq(PdxInstanceWrapper.ID_FIELD_NAME)); + verify(mockPdxInstance, never()).getField(eq(PdxInstanceWrapper.ID_FIELD_NAME)); + verify(wrapper, times(1)).getAtIdentifier(); + verifyNoMoreInteractions(mockPdxInstance); + } + + @Test + public void getAtIdentifierFromPdxInstance() { + + PdxInstance mockPdxInstance = mock(PdxInstance.class); + + PdxInstanceWrapper wrapper = spy(new PdxInstanceWrapper(mockPdxInstance)); + + doReturn(true).when(mockPdxInstance).hasField(eq(PdxInstanceWrapper.AT_IDENTIFIER_FIELD_NAME)); + doReturn(true).when(mockPdxInstance).hasField(eq("isbn")); + doReturn("isbn").when(mockPdxInstance).getField(eq(PdxInstanceWrapper.AT_IDENTIFIER_FIELD_NAME)); + doReturn("123456789").when(mockPdxInstance).getField(eq("isbn")); + + assertThat(wrapper.getAtIdentifier()).isEqualTo("123456789"); + + verify(mockPdxInstance, times(1)) + .hasField(eq(PdxInstanceWrapper.AT_IDENTIFIER_FIELD_NAME)); + verify(mockPdxInstance, times(1)) + .getField(eq(PdxInstanceWrapper.AT_IDENTIFIER_FIELD_NAME)); + verify(mockPdxInstance, times(1)).hasField(eq("isbn")); + verify(mockPdxInstance, times(1)).getField(eq("isbn")); + verifyNoMoreInteractions(mockPdxInstance); + } + + @Test(expected = IllegalStateException.class) + public void getAtIdentifierFromPdxInstanceWithNoDeclaredIdentity() { + + PdxInstance mockPdxInstance = mock(PdxInstance.class); + + PdxInstanceWrapper wrapper = spy(new PdxInstanceWrapper(mockPdxInstance)); + + doReturn(Account.class.getName()).when(mockPdxInstance).getClassName(); + doReturn(false).when(mockPdxInstance).hasField(any()); + + try { + wrapper.getAtIdentifier(); + } + catch (IllegalStateException expected) { + + assertThat(expected).hasMessage("PdxInstance for type [%s] has no declared identifier", + Account.class.getName()); + assertThat(expected).hasNoCause(); + + throw expected; + } + finally { + verify(mockPdxInstance, times(1)).getClassName(); + verify(mockPdxInstance, times(2)) + .hasField(eq(PdxInstanceWrapper.AT_IDENTIFIER_FIELD_NAME)); + verify(mockPdxInstance, times(1)) + .hasField(eq(PdxInstanceWrapper.ID_FIELD_NAME)); + verify(mockPdxInstance, never()).getField(anyString()); + verifyNoMoreInteractions(mockPdxInstance); + } + } + + @Test(expected = IllegalStateException.class) + public void getAtIdentifierFromPdxInstanceWithNoId() { + + PdxInstance mockPdxInstance = mock(PdxInstance.class); + + PdxInstanceWrapper wrapper = spy(new PdxInstanceWrapper(mockPdxInstance)); + + doReturn(Account.class.getName()).when(mockPdxInstance).getClassName(); + doReturn(true).when(mockPdxInstance).hasField(eq(PdxInstanceWrapper.ID_FIELD_NAME)); + + try { + wrapper.getAtIdentifier(); + } + catch (IllegalStateException expected) { + + assertThat(expected).hasMessage("PdxInstance for type [%s] has no id", + Account.class.getName()); + assertThat(expected).hasNoCause(); + + throw expected; + } + finally { + verify(mockPdxInstance, times(1)).getClassName(); + verify(mockPdxInstance, times(1)) + .hasField(eq(PdxInstanceWrapper.AT_IDENTIFIER_FIELD_NAME)); + verify(mockPdxInstance, times(1)) + .hasField(eq(PdxInstanceWrapper.ID_FIELD_NAME)); + verify(mockPdxInstance, never()).getField(any()); + verifyNoMoreInteractions(mockPdxInstance); + } + } + + @Test(expected = IllegalStateException.class) + public void getAtIdentifierFromPdxInstanceWithValidAtIdentifierAndIdentifierFieldButNoId() { + + PdxInstance mockPdxInstance = mock(PdxInstance.class); + + PdxInstanceWrapper wrapper = spy(new PdxInstanceWrapper(mockPdxInstance)); + + doReturn(Person.class.getName()).when(mockPdxInstance).getClassName(); + doReturn(true).when(mockPdxInstance).hasField(eq(PdxInstanceWrapper.AT_IDENTIFIER_FIELD_NAME)); + doReturn(false).when(mockPdxInstance).hasField(eq(PdxInstanceWrapper.ID_FIELD_NAME)); + doReturn(true).when(mockPdxInstance).hasField(eq("ssn")); + doReturn("ssn").when(mockPdxInstance).getField(eq(PdxInstanceWrapper.AT_IDENTIFIER_FIELD_NAME)); + doReturn(null).when(mockPdxInstance).getField(eq("ssn")); + + try { + wrapper.getAtIdentifier(); + } + catch (IllegalStateException expected) { + + String expectedMessage = "PdxInstance for type [%s] has no value [null] for field [ssn] declared in [%s]"; + + assertThat(expected).hasMessage(expectedMessage, Person.class.getName(), + PdxInstanceWrapper.AT_IDENTIFIER_FIELD_NAME); + assertThat(expected).hasNoCause(); + + throw expected; + } + finally { + verify(mockPdxInstance, times(1)).getClassName(); + verify(mockPdxInstance, times(2)).hasField(eq(PdxInstanceWrapper.AT_IDENTIFIER_FIELD_NAME)); + verify(mockPdxInstance, times(1)).hasField(eq(PdxInstanceWrapper.ID_FIELD_NAME)); + verify(mockPdxInstance, times(2)).hasField(eq("ssn")); + verify(mockPdxInstance, times(2)).getField(eq(PdxInstanceWrapper.AT_IDENTIFIER_FIELD_NAME)); + verify(mockPdxInstance, times(2)).getField(eq("ssn")); + verifyNoMoreInteractions(mockPdxInstance); + } + } + + @Test(expected = IllegalStateException.class) + public void getAtIdentifierFromPdxInstanceWithAtIdentifierReferringToInvalidIdentifierField() { + + PdxInstance mockPdxInstance = mock(PdxInstance.class); + + PdxInstanceWrapper wrapper = spy(new PdxInstanceWrapper(mockPdxInstance)); + + doReturn(Person.class.getName()).when(mockPdxInstance).getClassName(); + doReturn(true).when(mockPdxInstance).hasField(eq(PdxInstanceWrapper.AT_IDENTIFIER_FIELD_NAME)); + doReturn(false).when(mockPdxInstance).hasField(eq(PdxInstanceWrapper.ID_FIELD_NAME)); + doReturn(false).when(mockPdxInstance).hasField(eq("ssn")); + doReturn("ssn").when(mockPdxInstance).getField(eq(PdxInstanceWrapper.AT_IDENTIFIER_FIELD_NAME)); + + try { + wrapper.getAtIdentifier(); + } + catch (IllegalStateException expected) { + + assertThat(expected).hasMessage("PdxInstance for type [%s] has no field [ssn] declared in [%s]", + Person.class.getName(), PdxInstanceWrapper.AT_IDENTIFIER_FIELD_NAME); + assertThat(expected).hasNoCause(); + + throw expected; + } + finally { + verify(mockPdxInstance, times(1)).getClassName(); + verify(mockPdxInstance, times(2)).hasField(eq(PdxInstanceWrapper.AT_IDENTIFIER_FIELD_NAME)); + verify(mockPdxInstance, times(1)).hasField(eq(PdxInstanceWrapper.ID_FIELD_NAME)); + verify(mockPdxInstance, times(2)).hasField(eq("ssn")); + verify(mockPdxInstance, times(2)).getField(eq(PdxInstanceWrapper.AT_IDENTIFIER_FIELD_NAME)); + verify(mockPdxInstance, never()).getField(eq("ssn")); + verifyNoMoreInteractions(mockPdxInstance); + } + } + @Test public void getObjectReturnsObject() throws JsonProcessingException { @@ -481,6 +792,8 @@ public class PdxInstanceWrapperUnitTests { String getName(); } + interface Person { } + interface SendablePdxInstance extends PdxInstance, Sendable { } } diff --git a/spring-geode/src/main/java/org/springframework/geode/data/json/JsonCacheDataImporterExporter.java b/spring-geode/src/main/java/org/springframework/geode/data/json/JsonCacheDataImporterExporter.java index b4ab1dc0..4836df86 100644 --- a/spring-geode/src/main/java/org/springframework/geode/data/json/JsonCacheDataImporterExporter.java +++ b/spring-geode/src/main/java/org/springframework/geode/data/json/JsonCacheDataImporterExporter.java @@ -15,8 +15,6 @@ */ package org.springframework.geode.data.json; -import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalStateException; - import java.io.BufferedWriter; import java.io.ByteArrayOutputStream; import java.io.File; @@ -25,7 +23,6 @@ import java.io.IOException; import java.io.InputStream; import java.io.Writer; import java.util.Arrays; -import java.util.Objects; import java.util.Optional; import org.apache.geode.cache.Region; @@ -42,6 +39,7 @@ import org.springframework.geode.data.CacheDataImporter; import org.springframework.geode.data.json.converter.AbstractObjectArrayToJsonConverter; import org.springframework.geode.data.json.converter.JsonToPdxArrayConverter; import org.springframework.geode.data.json.converter.support.JacksonJsonToPdxConverter; +import org.springframework.geode.pdx.ObjectPdxInstanceAdapter; import org.springframework.geode.pdx.PdxInstanceWrapper; import org.springframework.geode.util.CacheUtils; import org.springframework.lang.NonNull; @@ -81,11 +79,8 @@ public class JsonCacheDataImporterExporter extends AbstractCacheDataImporterExpo private static final int CONTENT_PREVIEW_LENGTH = 50; private static final int DEFAULT_BUFFER_SIZE = 32768; - protected static final String AT_IDENTIFIER_FIELD_NAME = PdxInstanceWrapper.AT_IDENTIFIER_FIELD_NAME; protected static final String CLASSPATH_RESOURCE_PREFIX = ResourceLoader.CLASSPATH_URL_PREFIX; protected static final String FILESYSTEM_RESOURCE_PREFIX = "file://"; - protected static final String ID_FIELD_NAME = PdxInstanceWrapper.ID_FIELD_NAME; - protected static final String NO_FIELD_NAME = ""; protected static final String RESOURCE_NAME_PATTERN = "data-%s.json"; private JsonToPdxArrayConverter jsonToPdxArrayConverter = newJsonToPdxArrayConverter(); @@ -182,7 +177,7 @@ public class JsonCacheDataImporterExporter extends AbstractCacheDataImporterExpo .map(this::toPdx) .map(Arrays::stream) .ifPresent(pdxInstances -> pdxInstances.forEach(pdxInstance -> - region.put(getIdentifier(pdxInstance), postProcess(pdxInstance)))); + region.put(resolveKey(pdxInstance), resolveValue(pdxInstance)))); return region; } @@ -223,107 +218,6 @@ public class JsonCacheDataImporterExporter extends AbstractCacheDataImporterExpo } } - /** - * Determines the {@link Object identifier} (a.k.a. {@link PdxInstance#isIdentityField(String) identity}) - * for the given {@link PdxInstance}. - * - * @param pdxInstance {@link PdxInstance} to evaluate; must not be {@literal null}. - * @return the {@link Object identifier} for the given {@link PdxInstance}; never {@literal null}. - * @throws IllegalArgumentException if {@link PdxInstance} is {@literal null}. - * @throws IllegalStateException if the {@link PdxInstance} does not have an id. - * @see org.apache.geode.pdx.PdxInstance - * @see #getId(PdxInstance) - */ - protected @NonNull Object getIdentifier(@NonNull PdxInstance pdxInstance) { - - Assert.notNull(pdxInstance, "PdxInstance must not be null"); - - Optional idFieldName = CollectionUtils.nullSafeList(pdxInstance.getFieldNames()).stream() - .filter(StringUtils::hasText) - .filter(pdxInstance::isIdentityField) - .findFirst(); - - return idFieldName - .map(pdxInstance::getField) - .orElseGet(() -> getId(pdxInstance)); - } - - /** - * Searches for a PDX {@link String field name} called {@literal id} on the given {@link PdxInstance} - * and returns its {@link Object value} as the {@link Object identifier} for the {@link PdxInstance}. - * - * @param pdxInstance {@link PdxInstance} to evaluate; must not be {@literal null}. - * @return the {@link Object value} of the {@literal id} {@link String field} on the {@link PdxInstance}. - * @throws IllegalStateException if the {@link PdxInstance} does not have an id. - * @see org.apache.geode.pdx.PdxInstance - * @see #getAtIdentifier(PdxInstance) - */ - protected @NonNull Object getId(@NonNull PdxInstance pdxInstance) { - - Assert.notNull(pdxInstance, "PdxInstance must not be null"); - - return Optional.of(pdxInstance) - .filter(it -> it.hasField(ID_FIELD_NAME)) - .map(it -> it.getField(ID_FIELD_NAME)) - .orElseGet(() -> getAtIdentifier(pdxInstance)); - } - - /** - * Searches for a PDX {@link String field} declared by the {@literal @identifier} metadata {@link String field} - * on the given {@link PdxInstance} and returns the {@link Object value} of this {@link String field} - * as the {@link Object identifier} for the {@link PdxInstance}. - * - * @param pdxInstance {@link PdxInstance} to evaluate; most not be {@literal null}. - * @return the {@link Object value} of the {@link String field} declared in the {@literal @identifier} metadata - * {@link String field} on the {@link PdxInstance}. - * @throws IllegalArgumentException if {@link PdxInstance} is {@literal null}. - * @throws IllegalStateException if the {@link PdxInstance} does not have an id. - * @see org.apache.geode.pdx.PdxInstance - */ - protected @NonNull Object getAtIdentifier(@NonNull PdxInstance pdxInstance) { - - Assert.notNull(pdxInstance, "PdxInstance must not be null"); - - return Optional.of(pdxInstance) - .filter(pdx -> pdx.hasField(AT_IDENTIFIER_FIELD_NAME)) - .map(pdx -> pdx.getField(AT_IDENTIFIER_FIELD_NAME)) - .map(String::valueOf) - .filter(pdxInstance::hasField) - .map(pdxInstance::getField) - .orElseThrow(() -> newIllegalStateException(String.format("PdxInstance for type [%1$s] has no %2$s", - pdxInstance.getClassName(), resolveMessageForIdentifierError(pdxInstance)))); - } - - private @NonNull String resolveMessageForIdentifierError(@NonNull PdxInstance pdxInstance) { - - String message = "declared identifier"; - - if (pdxInstance.hasField(ID_FIELD_NAME)) { - message = "id"; - } - else if (pdxInstance.hasField(AT_IDENTIFIER_FIELD_NAME)) { - - Object atIdentifierFieldValue = pdxInstance.getField(AT_IDENTIFIER_FIELD_NAME); - - String resolvedIdentifierFieldName = Objects.nonNull(atIdentifierFieldValue) - ? atIdentifierFieldValue.toString().trim() - : NO_FIELD_NAME; - - boolean identifierFieldNameWasDeclaredAndIsValid = pdxInstance.hasField(resolvedIdentifierFieldName); - - Object identifier = identifierFieldNameWasDeclaredAndIsValid - ? pdxInstance.getField(resolvedIdentifierFieldName) - : null; - - message = identifierFieldNameWasDeclaredAndIsValid - ? String.format("value [%s] for field [%s] declared in [%s]", identifier, resolvedIdentifierFieldName, - AT_IDENTIFIER_FIELD_NAME) - : String.format("field [%s] declared in [%s]", resolvedIdentifierFieldName, AT_IDENTIFIER_FIELD_NAME); - } - - return message; - } - /** * Returns a computed JSON {@link Resource} containing JSON data to be loaded into the given {@link Region}. * @@ -372,6 +266,41 @@ public class JsonCacheDataImporterExporter extends AbstractCacheDataImporterExpo return pdxInstance; } + /** + * Resolves the {@link Object key} used to map the given {@link PdxInstance} as the {@link Object value} + * for the {@link Region.Entry entry} stored in the {@link Region}. + * + * @param pdxInstance {@link PdxInstance} used to resolve the {@link Object key}. + * @return the resolved {@link Object key}. + * @see org.springframework.geode.pdx.PdxInstanceWrapper#getIdentifier() + * @see org.apache.geode.pdx.PdxInstance + */ + protected @NonNull Object resolveKey(@NonNull PdxInstance pdxInstance) { + return PdxInstanceWrapper.from(pdxInstance).getIdentifier(); + } + + /** + * Resolves the {@link Object value} to store in the {@link Region} from the given {@link PdxInstance}. + * + * If the given {@link PdxInstance} is an instance of {@link PdxInstanceWrapper} then this method will return + * the underlying, {@link PdxInstanceWrapper#getDelegate() delegate} {@link PdxInstance}. + * + * If the given {@link PdxInstance} is an instance of {@link ObjectPdxInstanceAdapter} then this method will return + * the underlying, {@link ObjectPdxInstanceAdapter#getObject() Object}. + * + * Otherwise, the given {@link PdxInstance} is returned. + * + * @param pdxInstance {@link PdxInstance} to unwrap. + * @return the resolved {@link Object value}. + * @see org.springframework.geode.pdx.ObjectPdxInstanceAdapter#unwrap(PdxInstance) + * @see org.springframework.geode.pdx.PdxInstanceWrapper#unwrap(PdxInstance) + * @see org.apache.geode.pdx.PdxInstance + * @see #postProcess(PdxInstance) + */ + protected @Nullable Object resolveValue(@Nullable PdxInstance pdxInstance) { + return ObjectPdxInstanceAdapter.unwrap(PdxInstanceWrapper.unwrap(postProcess(pdxInstance))); + } + /** * Convert {@link Object values} contained in the {@link Region} to {@link String JSON}. * diff --git a/spring-geode/src/test/java/org/springframework/geode/data/json/JsonCacheDataImporterExporterUnitTests.java b/spring-geode/src/test/java/org/springframework/geode/data/json/JsonCacheDataImporterExporterUnitTests.java index 066bbfd4..c57f6520 100644 --- a/spring-geode/src/test/java/org/springframework/geode/data/json/JsonCacheDataImporterExporterUnitTests.java +++ b/spring-geode/src/test/java/org/springframework/geode/data/json/JsonCacheDataImporterExporterUnitTests.java @@ -37,8 +37,6 @@ import java.io.File; import java.io.IOException; import java.io.StringWriter; import java.io.Writer; -import java.util.Arrays; -import java.util.Collections; import java.util.Optional; import org.junit.Test; @@ -56,9 +54,6 @@ import org.springframework.dao.DataAccessResourceFailureException; import org.springframework.data.gemfire.util.ArrayUtils; import org.springframework.geode.data.json.converter.JsonToPdxArrayConverter; -import example.app.crm.model.Customer; -import example.app.pos.model.LineItem; - /** * Unit Tests for {@link JsonCacheDataImporterExporter}. * @@ -229,8 +224,8 @@ public class JsonCacheDataImporterExporterUnitTests { doReturn(true).when(mockResource).exists(); doReturn(json).when(this.importer).getContent(eq(mockResource)); doReturn(ArrayUtils.asArray(mockPdxInstanceOne, mockPdxInstanceTwo)).when(this.importer).toPdx(eq(json)); - doReturn(1).when(this.importer).getIdentifier(eq(mockPdxInstanceOne)); - doReturn(2).when(this.importer).getIdentifier(eq(mockPdxInstanceTwo)); + doReturn(1).when(this.importer).resolveKey(eq(mockPdxInstanceOne)); + doReturn(2).when(this.importer).resolveKey(eq(mockPdxInstanceTwo)); assertThat(this.importer.doImportInto(mockRegion)).isEqualTo(mockRegion); @@ -238,10 +233,12 @@ public class JsonCacheDataImporterExporterUnitTests { .getResource(eq(mockRegion), eq(JsonCacheDataImporterExporter.CLASSPATH_RESOURCE_PREFIX)); verify(this.importer, times(1)).getContent(eq(mockResource)); verify(this.importer, times(1)).toPdx(eq(json)); + verify(this.importer, times(1)).resolveKey(eq(mockPdxInstanceOne)); verify(this.importer, times(1)).postProcess(eq(mockPdxInstanceOne)); - verify(this.importer, times(1)).getIdentifier(eq(mockPdxInstanceOne)); + verify(this.importer, times(1)).resolveValue(eq(mockPdxInstanceOne)); + verify(this.importer, times(1)).resolveKey(eq(mockPdxInstanceTwo)); verify(this.importer, times(1)).postProcess(eq(mockPdxInstanceTwo)); - verify(this.importer, times(1)).getIdentifier(eq(mockPdxInstanceTwo)); + verify(this.importer, times(1)).resolveValue(eq(mockPdxInstanceTwo)); verify(mockResource, times(1)).exists(); verify(mockRegion, times(1)).put(eq(1), eq(mockPdxInstanceOne)); verify(mockRegion, times(1)).put(eq(2), eq(mockPdxInstanceTwo)); @@ -411,345 +408,6 @@ public class JsonCacheDataImporterExporterUnitTests { } } - @Test - public void getIdentifierFromPdxInstanceHavingIdentity() { - - PdxInstance mockPdxInstance = mock(PdxInstance.class); - - doReturn(Arrays.asList("age", "id", "name")).when(mockPdxInstance).getFieldNames(); - doReturn(true).when(mockPdxInstance).isIdentityField(eq("id")); - doReturn(42).when(mockPdxInstance).getField(eq("id")); - - assertThat(this.importer.getIdentifier(mockPdxInstance)).isEqualTo(42); - - verify(this.importer, never()).getId(any(PdxInstance.class)); - verify(mockPdxInstance, times(1)).getFieldNames(); - verify(mockPdxInstance, times(1)).isIdentityField(eq("age")); - verify(mockPdxInstance, times(1)).isIdentityField(eq("id")); - verify(mockPdxInstance, never()).isIdentityField(eq("name")); - verify(mockPdxInstance, times(1)).getField(eq("id")); - } - - @Test - public void getIdentifierFromPdxInstanceHavingNoFields() { - - PdxInstance mockPdxInstance = mock(PdxInstance.class); - - doReturn(null).when(mockPdxInstance).getFieldNames(); - doReturn(69).when(this.importer).getId(eq(mockPdxInstance)); - - assertThat(this.importer.getIdentifier(mockPdxInstance)).isEqualTo(69); - - verify(this.importer, times(1)).getId(eq(mockPdxInstance)); - verify(mockPdxInstance, times(1)).getFieldNames(); - verify(mockPdxInstance, never()).isIdentityField(anyString()); - verify(mockPdxInstance, never()).getField(anyString()); - } - - @Test - public void getIdentifierFromPdxInstanceHavingNoIdentityFields() { - - PdxInstance mockPdxInstance = mock(PdxInstance.class); - - doReturn(Arrays.asList("", "age", null, "name", " ")).when(mockPdxInstance).getFieldNames(); - doReturn(false).when(mockPdxInstance).isIdentityField(any()); - doReturn(99).when(this.importer).getId(eq(mockPdxInstance)); - - assertThat(this.importer.getIdentifier(mockPdxInstance)).isEqualTo(99); - - verify(this.importer, times(1)).getId(eq(mockPdxInstance)); - verify(mockPdxInstance, times(1)).getFieldNames(); - verify(mockPdxInstance, times(1)).isIdentityField(eq("age")); - verify(mockPdxInstance, times(1)).isIdentityField(eq("name")); - verify(mockPdxInstance, never()).isIdentityField(isNull()); - verify(mockPdxInstance, never()).isIdentityField(eq("")); - verify(mockPdxInstance, never()).isIdentityField(eq(" ")); - verify(mockPdxInstance, never()).getField(anyString()); - } - - @Test(expected = IllegalStateException.class) - public void getIdentifierFromPdxInstanceWithNoIdentifier() { - - PdxInstance mockPdxInstance = mock(PdxInstance.class); - - doReturn(Collections.singletonList("name")).when(mockPdxInstance).getFieldNames(); - doReturn(false).when(mockPdxInstance).isIdentityField(anyString()); - doThrow(new IllegalStateException("NO ID")).when(this.importer).getId(eq(mockPdxInstance)); - - try { - this.importer.getIdentifier(mockPdxInstance); - } - catch (IllegalStateException expected) { - - assertThat(expected).hasMessage("NO ID"); - assertThat(expected).hasNoCause(); - - throw expected; - } - finally { - verify(mockPdxInstance, times(1)).getFieldNames(); - verify(mockPdxInstance, times(1)).isIdentityField(eq("name")); - verify(mockPdxInstance, never()).getField(anyString()); - } - } - - @Test(expected = IllegalArgumentException.class) - public void getIdentifierFromNullPdxInstance() { - - try { - this.importer.getIdentifier(null); - } - catch (IllegalArgumentException expected) { - - assertThat(expected).hasMessage("PdxInstance must not be null"); - assertThat(expected).hasNoCause(); - - throw expected; - } - } - - @Test - public void getIdFromPdxInstanceHavingIdField() { - - PdxInstance mockPdxInstance = mock(PdxInstance.class); - - doReturn(true).when(mockPdxInstance).hasField(eq(JsonCacheDataImporterExporter.ID_FIELD_NAME)); - doReturn(42).when(mockPdxInstance).getField(eq(JsonCacheDataImporterExporter.ID_FIELD_NAME)); - - assertThat(this.importer.getId(mockPdxInstance)).isEqualTo(42); - - verify(mockPdxInstance, times(1)) - .hasField(eq(JsonCacheDataImporterExporter.ID_FIELD_NAME)); - verify(mockPdxInstance, times(1)) - .getField(eq(JsonCacheDataImporterExporter.ID_FIELD_NAME)); - verify(this.importer, never()).getAtIdentifier(any()); - } - - @Test - public void getIdFromPdxInstanceHavingIdFieldWithNoValueCallsGetAtIdentifier() { - - PdxInstance mockPdxInstance = mock(PdxInstance.class); - - doReturn(true).when(mockPdxInstance).hasField(eq(JsonCacheDataImporterExporter.ID_FIELD_NAME)); - doReturn(null).when(mockPdxInstance).getField(eq(JsonCacheDataImporterExporter.ID_FIELD_NAME)); - doReturn(69).when(this.importer).getAtIdentifier(eq(mockPdxInstance)); - - assertThat(this.importer.getId(mockPdxInstance)).isEqualTo(69); - - verify(mockPdxInstance, times(1)) - .hasField(eq(JsonCacheDataImporterExporter.ID_FIELD_NAME)); - verify(mockPdxInstance, times(1)) - .getField(eq(JsonCacheDataImporterExporter.ID_FIELD_NAME)); - verify(this.importer, times(1)).getAtIdentifier(any()); - } - - @Test - public void getIdFromPdxInstanceWithNoIdFieldCallsGetAtIdentifier() { - - PdxInstance mockPdxInstance = mock(PdxInstance.class); - - doReturn(false).when(mockPdxInstance).hasField(any()); - doReturn(99).when(this.importer).getAtIdentifier(eq(mockPdxInstance)); - - assertThat(this.importer.getId(mockPdxInstance)).isEqualTo(99); - - verify(mockPdxInstance, times(1)) - .hasField(eq(JsonCacheDataImporterExporter.ID_FIELD_NAME)); - verify(mockPdxInstance, never()).getField(eq(JsonCacheDataImporterExporter.ID_FIELD_NAME)); - verify(this.importer, times(1)).getAtIdentifier(eq(mockPdxInstance)); - } - - @Test(expected = IllegalArgumentException.class) - public void getIdFromNullPdxInstance() { - - try { - this.importer.getId(null); - } - catch (IllegalArgumentException expected) { - - assertThat(expected).hasMessage("PdxInstance must not be null"); - assertThat(expected).hasNoCause(); - - throw expected; - } - } - - @Test - public void getAtIdentifierFromPdxInstance() { - - PdxInstance mockPdxInstance = mock(PdxInstance.class); - - doReturn(true).when(mockPdxInstance) - .hasField(eq(JsonCacheDataImporterExporter.AT_IDENTIFIER_FIELD_NAME)); - doReturn(true).when(mockPdxInstance).hasField(eq("isbn")); - doReturn("isbn").when(mockPdxInstance) - .getField(eq(JsonCacheDataImporterExporter.AT_IDENTIFIER_FIELD_NAME)); - doReturn("123456789").when(mockPdxInstance).getField(eq("isbn")); - - assertThat(this.importer.getAtIdentifier(mockPdxInstance)).isEqualTo("123456789"); - - verify(mockPdxInstance, times(1)) - .hasField(eq(JsonCacheDataImporterExporter.AT_IDENTIFIER_FIELD_NAME)); - verify(mockPdxInstance, times(1)) - .getField(eq(JsonCacheDataImporterExporter.AT_IDENTIFIER_FIELD_NAME)); - verify(mockPdxInstance, times(1)).hasField(eq("isbn")); - verify(mockPdxInstance, times(1)).getField(eq("isbn")); - } - - @Test(expected = IllegalArgumentException.class) - public void getAtIdentifierWithNullPdxInstance() { - - try { - this.importer.getAtIdentifier(null); - } - catch (IllegalArgumentException expected) { - - assertThat(expected).hasMessage("PdxInstance must not be null"); - assertThat(expected).hasNoCause(); - - throw expected; - } - } - - @Test(expected = IllegalStateException.class) - public void getAtIdentifierFromPdxInstanceWithNoDeclaredIdentity() { - - PdxInstance mockPdxInstance = mock(PdxInstance.class); - - doReturn(LineItem.class.getName()).when(mockPdxInstance).getClassName(); - doReturn(false).when(mockPdxInstance).hasField(any()); - - try { - this.importer.getAtIdentifier(mockPdxInstance); - } - catch (IllegalStateException expected) { - - assertThat(expected).hasMessage("PdxInstance for type [%s] has no declared identifier", - LineItem.class.getName()); - assertThat(expected).hasNoCause(); - - throw expected; - } - finally { - verify(mockPdxInstance, times(1)).getClassName(); - verify(mockPdxInstance, times(2)) - .hasField(eq(JsonCacheDataImporterExporter.AT_IDENTIFIER_FIELD_NAME)); - verify(mockPdxInstance, times(1)) - .hasField(eq(JsonCacheDataImporterExporter.ID_FIELD_NAME)); - verify(mockPdxInstance, never()).getField(anyString()); - } - } - - @Test(expected = IllegalStateException.class) - public void getAtIdentifierFromPdxInstanceWithNoId() { - - PdxInstance mockPdxInstance = mock(PdxInstance.class); - - doReturn(LineItem.class.getName()).when(mockPdxInstance).getClassName(); - doReturn(true).when(mockPdxInstance).hasField(eq(JsonCacheDataImporterExporter.ID_FIELD_NAME)); - - try { - this.importer.getAtIdentifier(mockPdxInstance); - } - catch (IllegalStateException expected) { - - assertThat(expected).hasMessage("PdxInstance for type [%s] has no id", - LineItem.class.getName()); - assertThat(expected).hasNoCause(); - - throw expected; - } - finally { - verify(mockPdxInstance, times(1)).getClassName(); - verify(mockPdxInstance, times(1)) - .hasField(eq(JsonCacheDataImporterExporter.AT_IDENTIFIER_FIELD_NAME)); - verify(mockPdxInstance, times(1)) - .hasField(eq(JsonCacheDataImporterExporter.ID_FIELD_NAME)); - verify(mockPdxInstance, never()).getField(any()); - } - } - - @Test(expected = IllegalStateException.class) - public void getAtIdentifierFromPdxInstanceWithValidAtIdentifierAndIdentifierFieldButNoId() { - - PdxInstance mockPdxInstance = mock(PdxInstance.class); - - doReturn(Customer.class.getName()).when(mockPdxInstance).getClassName(); - doReturn(true).when(mockPdxInstance) - .hasField(eq(JsonCacheDataImporterExporter.AT_IDENTIFIER_FIELD_NAME)); - doReturn(false).when(mockPdxInstance) - .hasField(eq(JsonCacheDataImporterExporter.ID_FIELD_NAME)); - doReturn(true).when(mockPdxInstance).hasField(eq("ssn")); - doReturn("ssn").when(mockPdxInstance) - .getField(eq(JsonCacheDataImporterExporter.AT_IDENTIFIER_FIELD_NAME)); - doReturn(null).when(mockPdxInstance).getField(eq("ssn")); - - try { - this.importer.getAtIdentifier(mockPdxInstance); - } - catch (IllegalStateException expected) { - - assertThat(expected) - .hasMessage("PdxInstance for type [%s] has no value [null] for field [ssn] declared in [%s]", - Customer.class.getName(), JsonCacheDataImporterExporter.AT_IDENTIFIER_FIELD_NAME); - assertThat(expected).hasNoCause(); - - throw expected; - } - finally { - verify(mockPdxInstance, times(1)).getClassName(); - verify(mockPdxInstance, times(2)) - .hasField(eq(JsonCacheDataImporterExporter.AT_IDENTIFIER_FIELD_NAME)); - verify(mockPdxInstance, times(1)) - .hasField(eq(JsonCacheDataImporterExporter.ID_FIELD_NAME)); - verify(mockPdxInstance, times(2)).hasField(eq("ssn")); - verify(mockPdxInstance, times(2)) - .getField(eq(JsonCacheDataImporterExporter.AT_IDENTIFIER_FIELD_NAME)); - verify(mockPdxInstance, times(2)).getField(eq("ssn")); - verifyNoMoreInteractions(mockPdxInstance); - } - } - - @Test(expected = IllegalStateException.class) - public void getAtIdentifierFromPdxInstanceWithAtIdentifierReferringToInvalidIdentifierField() { - - PdxInstance mockPdxInstance = mock(PdxInstance.class); - - doReturn(Customer.class.getName()).when(mockPdxInstance).getClassName(); - doReturn(true).when(mockPdxInstance) - .hasField(eq(JsonCacheDataImporterExporter.AT_IDENTIFIER_FIELD_NAME)); - doReturn(false).when(mockPdxInstance) - .hasField(eq(JsonCacheDataImporterExporter.ID_FIELD_NAME)); - doReturn(false).when(mockPdxInstance).hasField(eq("ssn")); - doReturn("ssn").when(mockPdxInstance) - .getField(eq(JsonCacheDataImporterExporter.AT_IDENTIFIER_FIELD_NAME)); - - try { - this.importer.getAtIdentifier(mockPdxInstance); - } - catch (IllegalStateException expected) { - - assertThat(expected) - .hasMessage("PdxInstance for type [%s] has no field [ssn] declared in [%s]", - Customer.class.getName(), JsonCacheDataImporterExporter.AT_IDENTIFIER_FIELD_NAME); - assertThat(expected).hasNoCause(); - - throw expected; - } - finally { - verify(mockPdxInstance, times(1)).getClassName(); - verify(mockPdxInstance, times(2)) - .hasField(eq(JsonCacheDataImporterExporter.AT_IDENTIFIER_FIELD_NAME)); - verify(mockPdxInstance, times(1)) - .hasField(eq(JsonCacheDataImporterExporter.ID_FIELD_NAME)); - verify(mockPdxInstance, times(2)).hasField(eq("ssn")); - verify(mockPdxInstance, times(2)) - .getField(eq(JsonCacheDataImporterExporter.AT_IDENTIFIER_FIELD_NAME)); - verify(mockPdxInstance, never()).getField(eq("ssn")); - verifyNoMoreInteractions(mockPdxInstance); - } - } - @Test public void getResourceFromRegionAndResourcePrefix() {