/* * Copyright 2010-2013 the original author or authors. * * Licensed 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 org.springframework.data.gemfire.function; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotSame; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; import org.apache.geode.cache.Cache; import org.apache.geode.cache.CacheFactory; import org.apache.geode.cache.execute.FunctionContext; import org.apache.geode.internal.cache.GemFireCacheImpl; import org.apache.geode.pdx.PdxInstance; import org.apache.geode.pdx.PdxInstanceFactory; import org.apache.geode.pdx.PdxReader; import org.apache.geode.pdx.PdxSerializer; import org.apache.geode.pdx.PdxWriter; import org.apache.geode.pdx.internal.PdxInstanceEnum; import org.apache.geode.pdx.internal.PdxInstanceFactoryImpl; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; /** * The PdxFunctionArgumentResolverTest class is a test suite of test cases testing the contract and functionality * of the PdxFunctionArgumentResolver class. * * @author John Blum * @see org.junit.Test * @see org.mockito.Mockito * @see org.springframework.data.gemfire.function.PdxFunctionArgumentResolver * @see org.apache.geode.cache.Cache * @see org.apache.geode.cache.execute.FunctionContext * @see org.apache.geode.pdx.PdxInstance * @see org.apache.geode.pdx.PdxSerializer * @see org.apache.geode.pdx.internal.PdxInstanceEnum * @since 1.5.2 */ @SuppressWarnings("unused") public class PdxFunctionArgumentResolverTest { private static Cache gemfireCache; private PdxFunctionArgumentResolver functionArgumentResolver; @BeforeClass public static void setupGemFire() { gemfireCache = new CacheFactory() .setPdxSerializer(new PersonPdxSerializer()) .setPdxReadSerialized(true) .set("name", PdxFunctionArgumentResolverTest.class.getSimpleName()) .set("mcast-port", "0") .set("log-level", "warning") .create(); } @AfterClass public static void tearDown() { gemfireCache.close(); gemfireCache = null; } protected Method getMethod(final Class<?> type, final String methodName, final Class<?>... parameterTypes) { try { return type.getDeclaredMethod(methodName, parameterTypes); } catch (NoSuchMethodException e) { throw new RuntimeException(String.format( "Failed to get method (%1$s) with signature (%2$s) on Class type (%3$s)!", methodName, getMethodSignature(methodName, parameterTypes), type.getClass().getName())); } } protected Object getMethodSignature(final String methodName, final Class<?>... parameterTypes) { StringBuilder methodSignature = new StringBuilder(methodName); int count = 0; methodSignature.append("("); for (Class parameterType : parameterTypes) { methodSignature.append(count++ > 0 ? ", :" : ":").append(parameterType.getSimpleName()); } methodSignature.append("):Void"); return methodSignature.toString(); } protected void assertArguments(final Object[] expectedArguments, final Object[] actualArguments) { assertNotNull(actualArguments); assertNotSame(expectedArguments, actualArguments); assertEquals(expectedArguments.length, actualArguments.length); for (int index = 0; index < expectedArguments.length; index++) { assertEquals(expectedArguments[index], actualArguments[index]); } } protected Person createPerson(final String firstName, final String lastName, final Gender gender) { return new Person(firstName, lastName, gender); } protected PdxInstance toPdxInstance(final Person person) { PdxInstanceFactory pdxInstanceFactory = gemfireCache.createPdxInstanceFactory(person.getClass().getName()); pdxInstanceFactory.writeString("firstName", person.getFirstName()); pdxInstanceFactory.writeString("lastName", person.getLastName()); pdxInstanceFactory.writeObject("gender", person.getGender()); return pdxInstanceFactory.create(); } protected PdxInstance toPdxInstance(final Map<String, Object> objectData) { PdxInstanceFactory pdxInstanceFactory = gemfireCache.createPdxInstanceFactory(objectData.get("@type").toString()); for (Map.Entry<String, Object> entry : objectData.entrySet()) { if (!"@type".equals(entry.getKey())) { pdxInstanceFactory.writeObject(entry.getKey(), entry.getValue()); } } return pdxInstanceFactory.create(); } protected PdxInstance toPdxInstance(final Enum enumeratedType) { return PdxInstanceFactoryImpl.createPdxEnum(enumeratedType.getClass().getName(), enumeratedType.name(), enumeratedType.ordinal(), (GemFireCacheImpl) gemfireCache); } @Test public void testResolveSimpleFunctionArguments() { functionArgumentResolver = new PdxFunctionArgumentResolver() { @Override public Method getFunctionAnnotatedMethod() { return getMethod(FunctionExecutions.class, "simpleMethod", Boolean.class, Character.class, Integer.class, Double.class, String.class); } }; Object[] expectedArguments = { Boolean.TRUE, 'C', 123, Math.PI, "TEST" }; FunctionContext mockFunctionContext = mock(FunctionContext.class, "testResolveSimpleFunctionArguments"); when(mockFunctionContext.getArguments()).thenReturn(expectedArguments); Object[] actualArguments = functionArgumentResolver.resolveFunctionArguments(mockFunctionContext); assertArguments(expectedArguments, actualArguments); } @Test public void testResolveNonSerializedApplicationDomainTypeFunctionArguments() { functionArgumentResolver = new PdxFunctionArgumentResolver() { @Override public Method getFunctionAnnotatedMethod() { return getMethod(FunctionExecutions.class, "nonSerializedMethod", Boolean.class, Person.class, Character.class, Person.class, Integer.class, Double.class, Gender.class, String.class); } }; Object[] expectedArguments = { Boolean.TRUE, createPerson("Jon", "Doe", Gender.MALE), 'C', createPerson("Jane", "Doe", Gender.FEMALE), 123, Math.PI, Gender.FEMALE, "test" }; FunctionContext mockFunctionContext = mock(FunctionContext.class, "testResolveNonSerializedApplicationDomainTypeFunctionArguments"); when(mockFunctionContext.getArguments()).thenReturn(expectedArguments); Object[] actualArguments = functionArgumentResolver.resolveFunctionArguments(mockFunctionContext); assertArguments(expectedArguments, actualArguments); } @Test public void testResolveSerializedApplicationDomainTypeFunctionArguments() { functionArgumentResolver = new PdxFunctionArgumentResolver() { @Override public Method getFunctionAnnotatedMethod() { return getMethod(FunctionExecutions.class, "serializedMethod", Boolean.class, Person.class, String.class, Gender.class); } }; Person jackHandy = createPerson("Jack", "Handy", Gender.MALE); Object[] serializedArguments = { Boolean.TRUE, toPdxInstance(jackHandy), "test", toPdxInstance(Gender.MALE) }; Object[] expectedArguments = { Boolean.TRUE, jackHandy, "test", Gender.MALE }; FunctionContext mockFunctionContext = mock(FunctionContext.class, "testResolveSerializedApplicationDomainTypeFunctionArguments"); when(mockFunctionContext.getArguments()).thenReturn(serializedArguments); Object[] actualArguments = functionArgumentResolver.resolveFunctionArguments(mockFunctionContext); assertArguments(expectedArguments, actualArguments); } @Test public void testResolveUnnecessaryDeserializationFunctionArguments() { functionArgumentResolver = new PdxFunctionArgumentResolver() { @Override public Method getFunctionAnnotatedMethod() { return getMethod(FunctionExecutions.class, "unnecessaryDeserializationMethod", Boolean.class, Object.class, String.class, PdxInstanceEnum.class); } }; Person sandyHandy = createPerson("Sandy", "Handy", Gender.FEMALE); Object[] expectedArguments = { Boolean.TRUE, toPdxInstance(sandyHandy), "test", toPdxInstance(Gender.FEMALE) }; FunctionContext mockFunctionContext = mock(FunctionContext.class, "testResolveUnnecessaryDeserializationFunctionArguments"); when(mockFunctionContext.getArguments()).thenReturn(expectedArguments); Object[] actualArguments = functionArgumentResolver.resolveFunctionArguments(mockFunctionContext); assertArguments(expectedArguments, actualArguments); } @Test public void testResolveUnresolvableApplicationDomainTypeFunctionArguments() { functionArgumentResolver = new PdxFunctionArgumentResolver() { @Override public Method getFunctionAnnotatedMethod() { return getMethod(FunctionExecutions.class, "unresolvableMethod", String.class, Object.class); } }; Map<String, Object> addressData = new HashMap<String, Object>(5); addressData.put("@type", "org.example.Address"); addressData.put("street", "100 Main St."); addressData.put("city", "Portland"); addressData.put("state", "OR"); addressData.put("zip", "12345"); Object[] expectedArguments = { "test", toPdxInstance(addressData) }; FunctionContext mockFunctionContext = mock(FunctionContext.class, "testResolveUnresolvableApplicationDomainTypeFunctionArguments"); when(mockFunctionContext.getArguments()).thenReturn(expectedArguments); Object[] actualArguments = functionArgumentResolver.resolveFunctionArguments(mockFunctionContext); assertArguments(expectedArguments, actualArguments); } public static interface FunctionExecutions { void simpleMethod(Boolean value1, Character value2, Integer value3, Double value4, String value5); void nonSerializedMethod(Boolean value1, Person person1, Character value2, Integer value3, Person person2, Double value4, Gender gender, String value5); void serializedMethod(Boolean value1, Person person, String value2, Gender gender); void unnecessaryDeserializationMethod(Boolean value1, Object person, String value2, PdxInstanceEnum gender); void unresolvableMethod(String value, Object pdxInstance); } public static enum Gender { FEMALE, MALE } public static class Person { private final Gender gender; private final String firstName; private final String lastName; public Person(final String firstName, final String lastName, final Gender gender) { Assert.hasText(firstName, "The person's first name must be specified!"); Assert.hasText(lastName, "The person's last name must be specified!"); Assert.notNull(gender, "The person's gender must be specified!"); this.firstName = firstName; this.lastName = lastName; this.gender = gender; } public String getFirstName() { return firstName; } public String getLastName() { return lastName; } public Gender getGender() { return gender; } @Override public boolean equals(final Object obj) { if (obj == this) { return true; } if (!(obj instanceof Person)) { return false; } Person that = (Person) obj; return ObjectUtils.nullSafeEquals(this.getFirstName(), that.getFirstName()) && ObjectUtils.nullSafeEquals(this.getLastName(), that.getLastName()) && ObjectUtils.nullSafeEquals(this.getGender(), that.getGender()); } @Override public int hashCode() { int hashValue = 17; hashValue = 37 * hashValue + ObjectUtils.nullSafeHashCode(getFirstName()); hashValue = 37 * hashValue + ObjectUtils.nullSafeHashCode(getLastName()); hashValue = 37 * hashValue + ObjectUtils.nullSafeHashCode(getGender()); return hashValue; } @Override public String toString() { return String.format("%1$s %2$s is a %3$s", getFirstName(), getLastName(), getGender()); } } public static class PersonPdxSerializer implements PdxSerializer { @Override public boolean toData(final Object obj, final PdxWriter out) { if (obj instanceof Person) { Person person = (Person) obj; out.writeString("firstName", person.getFirstName()); out.writeString("lastName", person.getLastName()); out.writeObject("gender", person.getGender()); return true; } return false; } @Override public Object fromData(final Class<?> type, final PdxReader in) { if (Person.class.isAssignableFrom(type)) { return new Person(in.readString("firstName"), in.readString("lastName"), (Gender) in.readObject("gender")); } return null; } } }