Last active
December 20, 2015 06:29
-
-
Save mp911de/6086572 to your computer and use it in GitHub Desktop.
Suite for WebService Delegators
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public interface ChoiceValuesService { | |
ChoiceValues getChoiceValues(String choiceValuesId, boolean useCache) throws ChoiceValuesNotFoundException; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@WebService(name = "ChoiceValuesWebService", | |
targetNamespace = WSConstants.Q_NAME_NAMESPACE_URI) | |
public interface ChoiceValuesWebService { | |
@WebMethod(operationName = "getChoiceValues") | |
ChoiceValues getChoiceValues(@WebParam(name = "choiceValuesId") String choiceValuesId, @WebParam(name = "useCache") boolean useCache) | |
throws ChoiceValuesNotFoundFault, TechnicalFault; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @author <a href="mailto:mpaluch@paluch.biz">Mark Paluch</a> | |
* @since 25.07.13 12:31 | |
*/ | |
public class ChoiceValuesWebServiceClient implements ChoiceValuesService { | |
@Inject | |
private ChoiceValuesWebService choiceValuesWebService; | |
@Override | |
public ChoiceValues getChoiceValues(String choiceValuesId, boolean useCache) throws ChoiceValuesNotFoundException { | |
try { | |
return choiceValuesWebService.getChoiceValues(choiceValuesId, useCache); | |
} catch (ChoiceValuesNotFoundFault e) { | |
throw new ChoiceValuesNotFoundException(e.getMessage()); // NOPMD | |
} catch (TechnicalFault e) { | |
throw new TechnicalException(e); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.util.List; | |
import javax.ejb.EJB; | |
import javax.jws.WebService; | |
import org.apache.log4j.Logger; | |
/** | |
* @author <a href="mailto:mpaluch@paluch.biz">Mark Paluch</a> | |
* @since 24.07.13 10:41 | |
*/ | |
@WebService(name = "ChoiceValuesWebService", | |
serviceName = "ChoiceValuesWebService", | |
portName = "ChoiceValuesWebServicePort", | |
targetNamespace = WSConstants.Q_NAME_NAMESPACE_URI, | |
endpointInterface = "de.paluch.delegate.ws.ChoiceValuesWebService") | |
public class ChoiceValuesWebServiceImpl implements ChoiceValuesWebService { | |
private Logger log = Logger.getLogger(getClass()); | |
@EJB | |
private ChoiceValuesService choiceValuesService; | |
@Override | |
public ChoiceValues getChoiceValues(String choiceValuesId, boolean useCache) throws ChoiceValuesNotFoundFault, TechnicalFault { | |
try { | |
return choiceValuesService.getChoiceValues(choiceValuesId, useCache); | |
} catch (ChoiceValuesNotFoundException e) { | |
throw new ChoiceValuesNotFoundFault(e); | |
} catch (RuntimeException e) { | |
logError(log, e, choiceValuesId, useCache); | |
throw new TechnicalFault(EJBExceptionUtil.unwrap(e)); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import static org.junit.Assert.assertEquals; | |
import static org.junit.Assert.fail; | |
import static org.mockito.Mockito.verify; | |
import static org.mockito.Mockito.when; | |
import java.lang.reflect.Array; | |
import java.lang.reflect.Field; | |
import java.lang.reflect.InvocationTargetException; | |
import java.lang.reflect.Method; | |
import java.util.*; | |
import javax.jws.WebService; | |
import org.junit.Before; | |
import org.junit.Test; | |
import org.junit.runner.RunWith; | |
import org.junit.runners.Parameterized; | |
import org.mockito.Mockito; | |
import org.mockito.internal.util.reflection.Fields; | |
import org.mockito.internal.util.reflection.InstanceField; | |
/** | |
* This Test performs full-branch testing on supplied delegates. All calls with all exceptions are tested. The test also checks whether | |
* faults are converted properly into exceptions and vice versa. | |
* | |
* @author <a href="mailto:mpaluch@paluch.biz">Mark Paluch</a> | |
* @since 25.07.13 12:44 | |
*/ | |
@RunWith(Parameterized.class) | |
public class WebserviceDelegatorsTest { | |
public static final String EXCEPTION_SUFFIX = "Exception"; | |
public static final String FAULT_SUFFIX = "Fault"; | |
@Parameterized.Parameters(name = "{1}.{4}") | |
public static java.util.Collection<Object[]> data() { | |
// WebService Impl to Facade-Class Mapping | |
Map<Class<?>, Class<?>> classes = new HashMap<>(); | |
// SOAP Services | |
classes.put(ChoiceValuesWebServiceImpl.class, ChoiceValuesService.class); | |
classes.put(ConfigurationWebServiceImpl.class, ConfigurationService.class); | |
classes.put(DocumentWebServiceImpl.class, DocumentService.class); | |
classes.put(DossierWebServiceImpl.class, DossierService.class); | |
classes.put(MailWebServiceImpl.class, MailService.class); | |
classes.put(RenditionWebServiceImpl.class, RenditionService.class); | |
classes.put(SecurityPrincipalWebServiceImpl.class, SecurityPrincipalService.class); | |
classes.put(SupplierWebServiceImpl.class, SupplierService.class); | |
classes.put(WorkflowWebServiceImpl.class, WorkflowService.class); | |
// SOAP Clients | |
classes.put(ChoiceValuesWebServiceClient.class, ChoiceValuesWebService.class); | |
classes.put(ConfigurationWebServiceClient.class, ConfigurationWebService.class); | |
classes.put(DocumentWebServiceClient.class, DocumentWebService.class); | |
classes.put(DossierWebServiceClient.class, DossierWebService.class); | |
classes.put(MailWebServiceClient.class, MailWebService.class); | |
classes.put(RenditionWebServiceClient.class, RenditionWebService.class); | |
classes.put(SecurityPrincipalWebServiceClient.class, SecurityPrincipalWebService.class); | |
classes.put(SupplierWebServiceClient.class, SupplierWebService.class); | |
classes.put(WorkflowWebServiceClient.class, WorkflowWebService.class); | |
return map(classes); | |
} | |
private static List<Object[]> map(Map<Class<?>, Class<?>> classes) { | |
List<Object[]> result = new ArrayList<>(); | |
for (Map.Entry<Class<?>, Class<?>> entry : classes.entrySet()) { | |
for (Method method : entry.getKey().getMethods()) { | |
if (method.getDeclaringClass() == Object.class) { | |
continue; | |
} | |
result.add(new Object[] { | |
entry.getKey(), entry.getKey().getSimpleName(), entry.getValue(), method, method.getName() | |
}); | |
} | |
} | |
return result; | |
} | |
private Class<?> wrapperClass; | |
private Class<?> delegateInterfaceClass; | |
private Object wrapperInstance; | |
private Object delegateMock; | |
private Method method; | |
private Class expectedTechnicalExceptionClass; | |
private boolean wrappingOfRuntimeExceptions = true; | |
/** | |
* | |
* @param wrapperClass | |
* @param simpleClassName | |
* just for Test-Name | |
* @param delegateInterfaceClass | |
* @param method | |
* @param methodName | |
* just for Test-Name | |
* @throws Exception | |
*/ | |
public WebserviceDelegatorsTest(Class<?> wrapperClass, String simpleClassName, Class<?> delegateInterfaceClass, Method method, | |
String methodName) throws Exception { | |
this.wrapperClass = wrapperClass; | |
this.delegateInterfaceClass = delegateInterfaceClass; | |
this.method = method; | |
delegateMock = Mockito.mock(delegateInterfaceClass, delegateInterfaceClass.getSimpleName()); | |
wrapperInstance = wrapperClass.newInstance(); | |
tryInjection(delegateInterfaceClass); | |
if (wrapperClass.getAnnotation(WebService.class) != null) { | |
expectedTechnicalExceptionClass = TechnicalFault.class; | |
} else { | |
expectedTechnicalExceptionClass = TechnicalException.class; | |
wrappingOfRuntimeExceptions = false; | |
} | |
} | |
private void tryInjection(Class<?> delegateInterfaceClass) { | |
boolean injected = false; | |
for (InstanceField field : Fields.allDeclaredFieldsOf(wrapperInstance).instanceFields()) { | |
if (field.jdkField().getType().equals(delegateInterfaceClass)) { | |
field.set(delegateMock); | |
injected = true; | |
} | |
} | |
if (!injected) { | |
throw new IllegalStateException("Could not inject mock of type " + delegateInterfaceClass + " . Perhaps wrong type?"); | |
} | |
} | |
@Before | |
public void before() throws Exception { | |
Mockito.reset(delegateMock); | |
} | |
/** | |
* Test the Happy Path. | |
* | |
* @throws Exception | |
*/ | |
@Test | |
public void testSimpleDelegation() throws Exception { | |
Object[] parameters = createParameters(method.getParameterTypes()); | |
Method deletgateMethod = delegateInterfaceClass.getMethod(method.getName(), method.getParameterTypes()); | |
Object expectedMethodResult = null; | |
boolean hasReturn = hasReturn(deletgateMethod); | |
// Setup Return-Value | |
if (hasReturn) { | |
expectedMethodResult = createValue(deletgateMethod.getReturnType()); | |
Object stubbingResult = deletgateMethod.invoke(delegateMock, parameters); | |
when(stubbingResult).thenReturn(expectedMethodResult); | |
} | |
Object result = method.invoke(wrapperInstance, parameters); | |
// Verify that delegated method was also invoked on the mock. | |
deletgateMethod.invoke(verify(delegateMock), parameters); | |
if (hasReturn) { | |
assertEquals("Expected return object is wrong, " + getClassAndMethodName(), expectedMethodResult, result); | |
} | |
} | |
/** | |
* Test the declared Exceptions from the delegate. Every exception has to be thrown. | |
* | |
* @throws Exception | |
*/ | |
@Test | |
public void testExceptions() throws Exception { | |
Object[] parameters = createParameters(method.getParameterTypes()); | |
Method deletgateMethod = delegateInterfaceClass.getMethod(method.getName(), method.getParameterTypes()); | |
for (Class exceptionType : deletgateMethod.getExceptionTypes()) { | |
Mockito.reset(delegateMock); | |
Object stubbingResult = deletgateMethod.invoke(delegateMock, parameters); | |
when(stubbingResult).thenThrow(exceptionType); | |
try { | |
Object result = method.invoke(wrapperInstance, parameters); | |
fail("Missing Exception on calling " + getClassAndMethodName() + ", tried to throw " + exceptionType); | |
} catch (InvocationTargetException e) { | |
if (isExceptionAlsoContainedInWrapper(exceptionType)) { | |
String simpleName = getTranscodedExceptionName(exceptionType); | |
assertEquals("Wrong Exception on calling " + getClassAndMethodName(), simpleName, e.getTargetException().getClass() | |
.getSimpleName()); | |
} else { | |
assertEquals("Wrong Exception on calling " + getClassAndMethodName(), expectedTechnicalExceptionClass, e | |
.getTargetException().getClass()); | |
} | |
} | |
} | |
} | |
/** | |
* Test behavior of Runtime-Exceptions. Sometimes they have to be wrapped. | |
* | |
* @throws Exception | |
*/ | |
@Test | |
public void testRuntimeException() throws Exception { | |
Object[] parameters = createParameters(method.getParameterTypes()); | |
Method deletgateMethod = delegateInterfaceClass.getMethod(method.getName(), method.getParameterTypes()); | |
Object stubbingResult = deletgateMethod.invoke(delegateMock, parameters); | |
when(stubbingResult).thenThrow(new NullPointerException()); | |
try { | |
method.invoke(wrapperInstance, parameters); | |
fail("Missing Exception on calling " + getClassAndMethodName() + ", tried to throw " + NullPointerException.class); | |
} catch (InvocationTargetException e) { | |
if (wrappingOfRuntimeExceptions) { | |
assertEquals("Wrong Exception on calling " + getClassAndMethodName(), expectedTechnicalExceptionClass, e | |
.getTargetException().getClass()); | |
} else { | |
assertEquals("Wrong Exception on calling " + getClassAndMethodName(), NullPointerException.class, e.getTargetException() | |
.getClass()); | |
} | |
} | |
} | |
private String getTranscodedExceptionName(Class exceptionType) { | |
String simpleName = null; | |
if (exceptionType.getSimpleName().contains(EXCEPTION_SUFFIX)) { | |
simpleName = exceptionType.getSimpleName().replaceAll(EXCEPTION_SUFFIX, FAULT_SUFFIX); | |
} | |
if (exceptionType.getSimpleName().contains(FAULT_SUFFIX)) { | |
simpleName = exceptionType.getSimpleName().replaceAll(FAULT_SUFFIX, EXCEPTION_SUFFIX); | |
} | |
return simpleName; | |
} | |
private boolean isExceptionAlsoContainedInWrapper(Class exceptionType) { | |
String wrapperExceptionName = getTranscodedExceptionName(exceptionType); | |
for (Class wrapperExceptionType : method.getExceptionTypes()) { | |
if (wrapperExceptionType.getSimpleName().equals(wrapperExceptionName)) { | |
return true; | |
} | |
} | |
return false; | |
} | |
private boolean hasReturn(Method deletgateMethod) { | |
boolean hasReturn = false; | |
if (deletgateMethod.getReturnType() != null && !deletgateMethod.getReturnType().equals(Void.class) | |
&& !deletgateMethod.getReturnType().equals(Void.TYPE)) { | |
hasReturn = true; | |
} | |
return hasReturn; | |
} | |
private String getClassAndMethodName() { | |
return method.getDeclaringClass().getSimpleName() + "." + method.getName(); | |
} | |
private Object[] createParameters(Class[] parameterTypes) throws IllegalAccessException { | |
Object result[] = new Object[parameterTypes.length]; | |
for (int i = 0; i < parameterTypes.length; i++) { | |
Class parameterType = parameterTypes[i]; | |
result[i] = createValue(parameterType); | |
} | |
return result; | |
} | |
private Object createValue(Class parameterType) throws IllegalAccessException { | |
if (parameterType == String.class) { | |
return "Test-String " + System.currentTimeMillis(); | |
} | |
if (parameterType == Integer.class || parameterType == Integer.TYPE) { | |
return Integer.valueOf((int) System.currentTimeMillis()); | |
} | |
if (parameterType == Long.class || parameterType == Long.TYPE) { | |
return Long.valueOf(System.currentTimeMillis()); | |
} | |
if (parameterType == Double.class || parameterType == Double.TYPE) { | |
return Double.valueOf(System.currentTimeMillis()); | |
} | |
if (parameterType == Boolean.class || parameterType == Boolean.TYPE) { | |
return Boolean.valueOf(System.currentTimeMillis() % 2 == 0); | |
} | |
if (parameterType.isEnum()) { | |
Field[] fields = parameterType.getFields(); | |
return fields[0].get(null); | |
} | |
if (parameterType.getPackage() == null) { | |
if (parameterType.isArray()) { | |
Object array = Array.newInstance(parameterType.getComponentType(), 1); | |
Object value = createValue(parameterType.getComponentType()); | |
Array.set(array, 0, value); | |
return array; | |
} | |
throw new UnsupportedOperationException("Package is null: " + parameterType); | |
} | |
// Create stubs for our packages. | |
if (parameterType.getPackage().getName().startsWith("de.paluch.delegate")) { | |
return Mockito.mock(parameterType); | |
} | |
if (parameterType.getName().startsWith("java.util.List")) { | |
return Mockito.mock(parameterType); | |
} | |
if (parameterType.getName().startsWith("java.util.Map")) { | |
return Mockito.mock(parameterType); | |
} | |
if (parameterType.getName().startsWith("java.util.Locale")) { | |
return new Locale("de", "DE"); | |
} | |
throw new UnsupportedOperationException("Parameter-Type" + parameterType | |
+ " not yet supported. Would you be so kind and implement it?"); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment