You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@aries.apache.org by ga...@apache.org on 2009/10/07 05:00:44 UTC
svn commit: r822565 [3/4] - in
/incubator/aries/trunk/testsupport/testsupport-unit/src/main/java/org/apache/aries:
mocks/ unittest/junit/ unittest/mocks/ unittest/mocks/annotations/
Modified: incubator/aries/trunk/testsupport/testsupport-unit/src/main/java/org/apache/aries/unittest/mocks/Skeleton.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/testsupport/testsupport-unit/src/main/java/org/apache/aries/unittest/mocks/Skeleton.java?rev=822565&r1=822564&r2=822565&view=diff
==============================================================================
--- incubator/aries/trunk/testsupport/testsupport-unit/src/main/java/org/apache/aries/unittest/mocks/Skeleton.java (original)
+++ incubator/aries/trunk/testsupport/testsupport-unit/src/main/java/org/apache/aries/unittest/mocks/Skeleton.java Wed Oct 7 03:00:43 2009
@@ -1,1353 +1,1353 @@
-/*
- * 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 org.apache.aries.unittest.mocks;
-
-import java.lang.ref.SoftReference;
-import java.lang.reflect.Field;
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-
-import junit.framework.Assert;
-import junit.framework.AssertionFailedError;
-
-import org.apache.aries.unittest.mocks.annotations.InjectSkeleton;
-import org.apache.aries.unittest.mocks.annotations.Singleton;
-
-/**
- * <p>The Skeleton class is an implementation of the
- * <code>java.lang.reflect.InvocationHandler</code> that can be used for
- * dynamic mock objects.
- * </p>
- *
- * <ol>
- * <li>The static newMock methods can be used to create completely new mock
- * objects backed by an entirely new skeleton.
- * </li>
- * <li>The static getSkeleton method can be used to obtain the skeleton
- * backing a given mock.
- * </li>
- * <li>The createMock methods can be used to create a new mock object based on
- * the skeleton that is invoked.
- * </li>
- * <li>The registerMethodCallHandler method can be used to register a handler
- * that will be invoked when a method is called.
- * </li>
- * <li>The registerReturnTypeHandler method can be used to register a handler
- * that will be invoked when a method with a specific return type is
- * invoked. It should be noted that registered ReturnTypeHandlers will be
- * invoked only if a method call handler has not been registered for the
- * method that was invoked.
- * </li>
- * <li>The setReturnValue method can be used to set a value that will be
- * returned when a method is invoked.
- * </li>
- * <li>The checkCalls methods can be used to determine if the methods in the
- * list should have been called. They return a boolean to indicate if the
- * expected calls occurred.
- * </li>
- * <li>The assertCalls method performs the same operation as the checkCalls,
- * but throws an junit.framework.AssertionFailedError if the calls don't
- * match. This intended for use within the junit test framework
- * </li>
- * <li>If no method call or return type handlers have been registered for a
- * call then if the return type is an interface then a mock that implements
- * that interface will be returned, otherwise null will be returned.
- * </li>
- * </ol>
- */
-public final class Skeleton implements InvocationHandler
-{
- /** A list of calls made on this skeleton */
- private List<MethodCall> _methodCalls;
- /** The invocation handler to call after MethodCall and ReturnType handlers */
- private DefaultInvocationHandler default_Handler;
- /** The method call handlers */
- private Map<MethodCall, MethodCallHandler> _callHandlers;
- /** The type handlers */
- private Map<Class<?>, ReturnTypeHandler> _typeHandlers;
- /** The parameter map */
- private Map<String, Object> _mockParameters;
- /** A Map of mock objects to Maps of properties */
- private Map<Object, Map<String, Object>> _objectProperties;
- /** A Map of exception notification listeners */
- private Map<Class<?>, List<ExceptionListener>> _notificationListeners;
- /** The template class used to create this Skeleton, may be null */
- private Object _template;
- /** Cached template objects */
- private static ConcurrentMap<Object, SoftReference<Object>> _singletonMocks = new ConcurrentHashMap<Object, SoftReference<Object>>();
-
- // Constructors
-
- /* ------------------------------------------------------------------------ */
- /* Skeleton constructor
- /* ------------------------------------------------------------------------ */
- /**
- * constructs the skeleton with the default method call handlers and the
- * default return type handlers.
- */
- private Skeleton()
- {
- reset();
- }
-
- // Static methods create methods
-
- /* ------------------------------------------------------------------------ */
- /* newMock method
- /* ------------------------------------------------------------------------ */
- /**
- * This method returns a completely new mock object backed by a new skeleton
- * object. It is equivalent to
- * <code>new Skeleton().createMock(interfaceClazzes)</code>
- *
- * @param interfaceClazzes the classes the mock should implement
- * @return the new mock object.
- */
- public final static Object newMock(Class<?> ... interfaceClazzes)
- {
- return new Skeleton().createMock(interfaceClazzes);
- }
-
- /* ------------------------------------------------------------------------ */
- /* newMock method
- /* ------------------------------------------------------------------------ */
- /**
- * This method returns a completely new mock object backed by a new skeleton
- * object. It is equivalent to
- * <code>new Skeleton().createMock(interfaceClazzes)</code>
- *
- * @param <T> The object type.
- * @param interfaceClazz the classes the mock should implement
- * @return the new mock object.
- */
- public final static <T> T newMock(Class<T> interfaceClazz)
- {
- return interfaceClazz.cast(new Skeleton().createMock(interfaceClazz));
- }
-
- /**
- * It is often the case that only a subset of methods on an interface are needed, but
- * those methods that are needed are quite complex. In this case a static mock forces
- * you into implementing lots of methods you do not need, and produces problems when
- * new methods are added to the interface being implemented. This method can essentially
- * be used to complete the interface implementation. The object passed in is an instance
- * of a class that implements a subset of the methods on the supplied interface. It does
- * not need to implement the interface itself. The returned object will implement the full
- * interface and delegate to the methods on the templateObject where necessary.
- *
- * @param <T> The object type.
- * @param template The template object for the mock
- * @param interfaceClazz The interface to implement
- * @return An implementation of the interface that delegates (where appropraite) onto the template.
- */
- public final static <T> T newMock(final Object template, Class<T> interfaceClazz)
- {
- Class<?> templateClass = template.getClass();
-
- if (templateClass.getAnnotation(Singleton.class) != null) {
- SoftReference<Object> mock = _singletonMocks.get(template);
- if (mock != null) {
- Object theMock = mock.get();
- if (theMock == null) {
- _singletonMocks.remove(template);
- } else if (interfaceClazz.isInstance(theMock)) {
- return interfaceClazz.cast(theMock);
- }
- }
- }
-
- Skeleton s = new Skeleton();
- s._template = template;
-
- for (Method m : interfaceClazz.getMethods()) {
- try {
- final Method m2 = templateClass.getMethod(m.getName(), m.getParameterTypes());
-
- MethodCall mc = new MethodCall(interfaceClazz, m.getName(), (Object[])m.getParameterTypes());
- s.registerMethodCallHandler(mc, new MethodCallHandler()
- {
- public Object handle(MethodCall methodCall, Skeleton parent) throws Exception
- {
-
- try {
- m2.setAccessible(true);
- return m2.invoke(template, methodCall.getArguments());
- } catch (InvocationTargetException ite) {
- if(ite.getTargetException() instanceof Exception)
- throw (Exception)ite.getTargetException();
- else throw new Exception(ite.getTargetException());
- }
- }
- });
- } catch (NoSuchMethodException e) {
- // do nothing here, it is a method not on the interface so ignore it.
- }
- }
-
- Field[] fs = template.getClass().getFields();
-
- for (Field f : fs) {
- InjectSkeleton sk = f.getAnnotation(InjectSkeleton.class);
-
- if (sk != null) {
- f.setAccessible(true);
- try {
- f.set(template, s);
- } catch (IllegalArgumentException e) {
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- }
- }
- }
-
- Object o = s.createMock(interfaceClazz);
- _singletonMocks.put(template, new SoftReference<Object>(o) {
- @Override
- public boolean enqueue()
- {
- _singletonMocks.remove(template);
-
- System.out.println("Done cleanup");
-
- return super.enqueue();
- }
- });
- return interfaceClazz.cast(o);
- }
-
- // static mock query methods
-
- /* ------------------------------------------------------------------------ */
- /* getSkeleton method
- /* ------------------------------------------------------------------------ */
- /**
- * This method returns the Skeleton backing the supplied mock object. If the
- * supplied object is not a mock an IllegalArgumentException will be thrown.
- *
- * @param mock the mock object
- * @return the skeleton backing the mock object
- * @throws IllegalArgumentException thrown if the object is not a mock.
- */
- public final static Skeleton getSkeleton(Object mock)
- throws IllegalArgumentException
- {
- InvocationHandler ih = Proxy.getInvocationHandler(mock);
- if (ih instanceof Skeleton)
- {
- return (Skeleton)ih;
- }
- throw new IllegalArgumentException("The supplied proxy (" + mock + ") was not a (Jetstream mediations) dynamic mock ");
- }
-
- /* ------------------------------------------------------------------------ */
- /* isSkeleton method
- /* ------------------------------------------------------------------------ */
- /**
- * This method returns true if and only the provided object is backed by a
- * Skeleton. Another way to think about this is if it returns true then a
- * call to getSkeleton will not result in an IllegalArgumentException, and is
- * guaranteed to return a Skeleton.
- *
- * @param mock the mock to test.
- * @return true if it is backed by a skeleton.
- */
- public final static boolean isSkeleton(Object mock)
- {
- if (Proxy.isProxyClass(mock.getClass())) {
- InvocationHandler ih = Proxy.getInvocationHandler(mock);
-
- return (ih instanceof Skeleton);
- }
-
- return false;
- }
-
- // InvocationHandler defined methods.
-
- /* ------------------------------------------------------------------------ */
- /* invoke method
- /* ------------------------------------------------------------------------ */
- /**
- * This method is invoked by the mock objects. It constructs a MethodCall
- * object representing the call and adds it to the list of calls that were
- * made. (It should be noted that if the method is toString, hashCode or
- * equals then they are not added to the list.) It then calls a registered
- * MethodCallHandler, if a MethodCallHandler is not registered then a
- * ReturnTypeHandler is invoked. If a ReturnTypeHandler is not invoked then
- * the registered default InvocationHandler is called. By default the Skeleton
- * is constructed with a DefaultInvocationHandler. If the invoked method has
- * an interface as a return type then the DefaultInvocationHandler will return
- * a new mock implementing that interface. If the return type is a class null
- * will be returned.
- *
- * @param targetObject The mock object that was invoked.
- * @param calledMethod The method that was called.
- * @param arguments The arguments that were passed.
- * @return The return of the method invoked.
- * @throws Throwable Any exception thrown.
- */
- public Object invoke(Object targetObject, Method calledMethod, Object[] arguments)
- throws Throwable
- {
- String methodName = calledMethod.getName();
- MethodCall call = new MethodCall(targetObject, methodName, arguments);
-
- if (!DefaultMethodCallHandlers.isDefaultMethodCall(call))
- {
- _methodCalls.add(call);
- }
-
- Object result;
-
- try
- {
- if (_callHandlers.containsKey(call))
- {
- MethodCallHandler handler = _callHandlers.get(call);
- result = handler.handle(call, this);
- }
- else if (isReadWriteProperty(targetObject.getClass(), calledMethod))
- {
- String propertyName = methodName.substring(3);
- if (methodName.startsWith("get") || methodName.startsWith("is"))
- {
- if (methodName.startsWith("is")) propertyName = methodName.substring(2);
-
- Map<String, Object> properties = _objectProperties.get(targetObject);
- if (properties == null)
- {
- properties = new HashMap<String, Object>();
- _objectProperties.put(targetObject, properties);
- }
-
- if (properties.containsKey(propertyName))
- {
- result = properties.get(propertyName);
- }
- else if (_typeHandlers.containsKey(calledMethod.getReturnType()))
- {
- result = createReturnTypeProxy(calledMethod.getReturnType());
- }
- else
- {
- result = default_Handler.invoke(targetObject, calledMethod, arguments);
- }
- }
- // Must be a setter.
- else
- {
- Map<String, Object> properties = _objectProperties.get(targetObject);
- if (properties == null)
- {
- properties = new HashMap<String, Object>();
- _objectProperties.put(targetObject, properties);
- }
-
- properties.put(propertyName, arguments[0]);
- result = null;
- }
- }
- else if (_typeHandlers.containsKey(calledMethod.getReturnType()))
- {
- result = createReturnTypeProxy(calledMethod.getReturnType());
- }
- else
- {
- result = default_Handler.invoke(targetObject, calledMethod, arguments);
- }
- }
- catch (Throwable t)
- {
- Class<?> throwableType = t.getClass();
- List<ExceptionListener> listeners = _notificationListeners.get(throwableType);
- if (listeners != null)
- {
- for (ExceptionListener listener : listeners)
- {
- listener.exceptionNotification(t);
- }
- }
-
- throw t;
- }
-
- return result;
- }
-
- // MethodCall registration methods
-
- /* ------------------------------------------------------------------------ */
- /* registerMethodCallHandler method
- /* ------------------------------------------------------------------------ */
- /**
- * This method registers a MethodCallHandler for the specified MethodCall.
- *
- * @param call The method that was called.
- * @param handler The MethodCallHandler.
- */
- public void registerMethodCallHandler(MethodCall call, MethodCallHandler handler)
- {
- deRegisterMethodCallHandler(call);
- _callHandlers.put(call, handler);
- }
-
- /* ------------------------------------------------------------------------ */
- /* deRegisterMethodCallHandler method
- /* ------------------------------------------------------------------------ */
- /**
- * This method removes a registered MethodCallHandler for the specified
- * MethodCall.
- *
- * @param call the specified MethodCall
- */
- public void deRegisterMethodCallHandler(MethodCall call)
- {
- _callHandlers.remove(call);
- }
-
- /* ------------------------------------------------------------------------ */
- /* reset method
- /* ------------------------------------------------------------------------ */
- /**
- * This method resets the skeleton to the state it was in prior just after it
- * was constructed.
- */
- public void reset()
- {
- _methodCalls = new LinkedList<MethodCall>();
- _callHandlers = new HashMap<MethodCall, MethodCallHandler>();
- _typeHandlers = new HashMap<Class<?>, ReturnTypeHandler>();
- DefaultReturnTypeHandlers.registerDefaultHandlers(this);
- DefaultMethodCallHandlers.registerDefaultHandlers(this);
- default_Handler = new DefaultInvocationHandler(this);
- _mockParameters = new HashMap<String, Object>();
- _objectProperties = new HashMap<Object, Map<String, Object>>();
- _notificationListeners = new HashMap<Class<?>, List<ExceptionListener>>();
- }
-
- /* ------------------------------------------------------------------------ */
- /* clearMethodCalls method
- /* ------------------------------------------------------------------------ */
- /**
- * This method clears the method calls list for the skeleton
- */
- public void clearMethodCalls()
- {
- _methodCalls = new LinkedList<MethodCall>();
- }
-
-
- /* ------------------------------------------------------------------------ */
- /* setReturnValue method
- /* ------------------------------------------------------------------------ */
- /**
- * This is a convenience method for registering a method call handler where
- * a specific value should be returned when a method is called, rather than
- * some logic needs to be applied. The value should be an object or the object
- * version of the primitive for the methods return type, so if the method
- * returns short the value must be an instance of java.lang.Short, not
- * java.lang.Integer.
- *
- * @param call the method being called.
- * @param value the value to be returned when that method is called.
- */
- public void setReturnValue(MethodCall call, final Object value)
- {
- Class<?> clazz;
- try {
- clazz = Class.forName(call.getClassName());
- } catch (ClassNotFoundException e) {
- throw new IllegalStateException("This should be impossible as we have already seen this class loaded");
- }
-
-
- Method[] methods = clazz.getMethods();
-
- methods: for (Method m : methods) {
- if(!!!m.getName().equals(call.getMethodName()))
- continue methods;
-
- Object[] args = call.getArguments();
- Class<?>[] parms = m.getParameterTypes();
-
- if (args.length == parms.length) {
- for (int i = 0; i < args.length; i++) {
- if (args[i] instanceof Class && args[i].equals(parms[i])) {
- } else if (parms[i].isInstance(args[i])) {
- } else {
- continue methods;
- }
- }
-
- Class<?> returnType = m.getReturnType();
- if (returnType.isPrimitive()) {
- if (returnType == boolean.class) returnType = Boolean.class;
- else if (returnType == byte.class) returnType = Byte.class;
- else if (returnType == short.class) returnType = Short.class;
- else if (returnType == char.class) returnType = Character.class;
- else if (returnType == int.class) returnType = Integer.class;
- else if (returnType == long.class) returnType = Long.class;
- else if (returnType == float.class) returnType = Float.class;
- else if (returnType == double.class) returnType = Double.class;
- }
-
- if (value != null && !!!returnType.isInstance(value)) {
- throw new IllegalArgumentException("The object cannot be returned by the requested method: " + call);
- } else break methods;
- }
- }
-
-
-
- registerMethodCallHandler(call, new MethodCallHandler()
- {
- public Object handle(MethodCall methodCall, Skeleton parent) throws Exception
- {
- return value;
- }
- });
- }
-
- /* ------------------------------------------------------------------------ */
- /* setThrow method
- /* ------------------------------------------------------------------------ */
- /**
- * This is a convenience method for registering a method call handler where
- * a specific exception should be thrown when the method is called, rather
- * than some logic needs to be applied.
- *
- * @param call the method being called
- * @param thingToThrow the exception to throw.
- */
- public void setThrows(MethodCall call, final Exception thingToThrow)
- {
- registerMethodCallHandler(call, new MethodCallHandler()
- {
- public Object handle(MethodCall methodCall, Skeleton parent) throws Exception
- {
- thingToThrow.fillInStackTrace();
- throw thingToThrow;
- }
- });
- }
-
- /* ------------------------------------------------------------------------ */
- /* setThrow method
- /* ------------------------------------------------------------------------ */
- /**
- * This is a convenience method for registering a method call handler where
- * a specific exception should be thrown when the method is called, rather
- * than some logic needs to be applied.
- *
- * @param call the method being called
- * @param thingToThrow the exception to throw.
- */
- public void setThrows(MethodCall call, final Error thingToThrow)
- {
- registerMethodCallHandler(call, new MethodCallHandler()
- {
- public Object handle(MethodCall methodCall, Skeleton parent) throws Exception
- {
- thingToThrow.fillInStackTrace();
- throw thingToThrow;
- }
- });
- }
-
- // ReturnType registration methods
-
- /* ------------------------------------------------------------------------ */
- /* registerReturnTypeHandler method
- /* ------------------------------------------------------------------------ */
- /**
- * This method registers a ReturnTypeHandler for the specified class.
- *
- * @param clazz The class to be handled.
- * @param handler The ReturnTypeHandler
- */
- public void registerReturnTypeHandler(Class<?> clazz, ReturnTypeHandler handler)
- {
- deRegisterReturnTypeHandler(clazz);
- _typeHandlers.put(clazz, handler);
- }
-
- /* ------------------------------------------------------------------------ */
- /* deRegisterReturnTypeHandler method
- /* ------------------------------------------------------------------------ */
- /**
- * This method removes a registration for a ReturnTypeHandler for the
- * specified class.
- *
- * @param clazz The class to deregister the handler for.
- */
- public void deRegisterReturnTypeHandler(Class<?> clazz)
- {
- _typeHandlers.remove(clazz);
- }
-
- // Exception notification methods
-
- /* ------------------------------------------------------------------------ */
- /* registerExceptionListener method
- /* ------------------------------------------------------------------------ */
- /**
- * This method registers an ExceptionListener when the specified Exception is
- * thrown.
- *
- * @param throwableType The type of the Throwable
- * @param listener The listener.
- */
- public void registerExceptionListener(Class<?> throwableType, ExceptionListener listener)
- {
- List<ExceptionListener> l = _notificationListeners.get(throwableType);
- if (l == null)
- {
- l = new ArrayList<ExceptionListener>();
- _notificationListeners.put(throwableType, l);
- }
- l.add(listener);
- }
-
- // parameter related methods
-
- /* ------------------------------------------------------------------------ */
- /* setParameter method
- /* ------------------------------------------------------------------------ */
- /**
- * This method allows a parameter to be set. It is intended to be used by
- * MethodCallHandlers and ReturnTypeHandlers.
- *
- * @param key The key
- * @param value The value
- */
- public void setParameter(String key, Object value)
- {
- _mockParameters.put(key, value);
- }
-
- /* ------------------------------------------------------------------------ */
- /* getParameter method
- /* ------------------------------------------------------------------------ */
- /**
- * This method allows a parameter to be retrieved.
- *
- * @param key the key the parameter was set using
- * @return the parameter
- */
- public Object getParameter(String key)
- {
- return _mockParameters.get(key);
- }
-
- /* ------------------------------------------------------------------------ */
- /* getTemplateObject method
- /* ------------------------------------------------------------------------ */
- /**
- * @return the template object, if one was used when initializing this skeleton.
- */
- public Object getTemplateObject()
- {
- return _template;
- }
-
- // default InvocationHandler related methods
-
- /**
- * @param defaultHandler The defaultHandler to set.
- */
- public void setDefaultHandler(DefaultInvocationHandler defaultHandler)
- {
- this.default_Handler = defaultHandler;
- }
-
- // MethodCall list check methods
-
- /* ------------------------------------------------------------------------ */
- /* checkCalls method
- /* ------------------------------------------------------------------------ */
- /**
- * This method checks that the calls in the list occurred. If the addCalls
- * boolean is true then their must be an exact match. If the allCalls boolean
- * is false then the calls in the list must occur in that order, but other
- * calls can be in between.
- *
- * @param calls The expected calls list
- * @param allCalls true if an exact match comparison should be performed
- * @return true if they the expected calls match.
- */
- public boolean checkCalls(List<MethodCall> calls, boolean allCalls)
- {
- boolean found = false;
- if (allCalls)
- {
- return calls.equals(_methodCalls);
- }
- else
- {
- Iterator<MethodCall> actual = _methodCalls.iterator();
- for (MethodCall expectedCall : calls)
- {
- found = false;
- actual: while (actual.hasNext())
- {
- MethodCall actualCall = actual.next();
- if (actualCall.equals(expectedCall))
- {
- found = true;
- break actual;
- }
- }
- }
- }
-
- return found;
- }
-
- /* ------------------------------------------------------------------------ */
- /* checkCall method
- /* ------------------------------------------------------------------------ */
- /**
- * Checks that the specified method has been called on this skeleton
- *
- * @param call the call that should have been called.
- * @return true if the MethodCall occurs in the list.
- */
- public boolean checkCall(MethodCall call)
- {
- return this._methodCalls.contains(call);
- }
-
- // MethodCall list assert methods
-
- /* ------------------------------------------------------------------------ */
- /* assertCalls method
- /* ------------------------------------------------------------------------ */
- /**
- * This method checks that the MethodCalls objects in the given list were
- * made and throws an AssertionFailedError if they were not. If allCalls is
- * true the given list and the calls list must be identical. If allCalls is
- * false other calls could have been made on the skeleton in between ones
- * specified in the list.
- *
- * @param calls the list of calls
- * @param allCalls whether an exact match between the lists is required
- * @throws AssertionFailedError if a failure has occurred.
- */
- public void assertCalled(List<MethodCall> calls, boolean allCalls) throws AssertionFailedError
- {
- if (allCalls)
- {
- if ((calls == null) && (_methodCalls == null)) return;
-
- if (calls == null)
- {
- throw new AssertionFailedError("expected null, but was " + _methodCalls);
- }
-
- if (_methodCalls == null)
- {
- throw new AssertionFailedError("expected:" + calls + " but was null");
- }
-
- if (calls.equals(_methodCalls)) return;
-
- // OK compare lists and decide on differences - initially all the lists are different
- int startOfDifferences = 0;
- // Remove the common start of sequence
- boolean lastItemSame = true;
-
- for (int i = 0; i < calls.size() && i < _methodCalls.size() && lastItemSame; i++)
- {
- if ((calls.get(i) == null) && (_methodCalls.get(i) == null))
- {
- lastItemSame = true;
- }
- else if ((calls.get(i) == null) || (_methodCalls.get(i) == null))
- {
- lastItemSame = false;
- }
- else
- {
- lastItemSame = calls.get(i).equals(_methodCalls.get(i));
- }
-
- if (lastItemSame) startOfDifferences++;
-
- }//for
- // Now remove the common bit at the end
- int endOfDifferencesInExpected = calls.size();
- int endOfDifferencesInReceived = _methodCalls.size();
- lastItemSame = true;
-
- while ((endOfDifferencesInExpected > startOfDifferences)
- && (endOfDifferencesInReceived > startOfDifferences)
- && lastItemSame)
- {
- int ap = endOfDifferencesInExpected - 1;
- int bp = endOfDifferencesInReceived - 1;
-
- if ((calls.get(ap) == null) && (_methodCalls.get(bp) == null))
- {
- lastItemSame = true;
- }
- else if ((calls.get(ap) == null) || (_methodCalls.get(bp) == null))
- {
- lastItemSame = false;
- }
- else
- {
- lastItemSame = calls.get(ap).equals(_methodCalls.get(bp));
- }
-
- if (lastItemSame)
- {
- endOfDifferencesInExpected--;
- endOfDifferencesInReceived--;
- }
-
- }//while
-
- String failureText;
- // OK, now build the failureText
- if (endOfDifferencesInExpected == startOfDifferences)
- {
- failureText =
- "Expected calls and actual calls differed because "
- + _methodCalls.subList(startOfDifferences, endOfDifferencesInReceived)
- + " inserted after element "
- + startOfDifferences;
-
- }
- else if (endOfDifferencesInReceived == startOfDifferences)
- {
- failureText =
- "Expected calls and actual calls differed because "
- + calls.subList(startOfDifferences, endOfDifferencesInExpected)
- + " missing after element "
- + startOfDifferences;
-
- }
- else
- {
- if ((endOfDifferencesInExpected == startOfDifferences + 1)
- && (endOfDifferencesInReceived == startOfDifferences + 1))
- {
-
- failureText =
- "Expected calls and actual calls differed because element "
- + startOfDifferences
- + " is different (calls:"
- + calls.get(startOfDifferences)
- + " but was:"+_methodCalls.get(startOfDifferences)+") ";
-
- }
- else if (endOfDifferencesInExpected == startOfDifferences + 1)
- {
-
- failureText =
- "Expected calls and actual calls differed because element "
- + startOfDifferences
- + " ("
- + calls.get(startOfDifferences)
- + ") has been replaced by "
- + _methodCalls.subList(startOfDifferences, endOfDifferencesInReceived);
- }
- else
- {
- failureText =
- "Expected calls and actual calls differed because elements between "
- + startOfDifferences
- + " and "
- + (endOfDifferencesInExpected - 1)
- + " are different (expected:"
- + calls.subList(startOfDifferences, endOfDifferencesInExpected)
- + " but was:"
- + _methodCalls.subList(startOfDifferences, endOfDifferencesInReceived)
- + ")";
- }//if
- }//if
-
- throw new AssertionFailedError(failureText + " expected:" + calls + " but was:" + _methodCalls);
- }
- else
- {
- Iterator<MethodCall> expected = calls.iterator();
- Iterator<MethodCall> actual = _methodCalls.iterator();
- while (expected.hasNext())
- {
- boolean found = false;
- MethodCall expectedCall = expected.next();
- MethodCall actualCall = null;
-
- actual: while (actual.hasNext())
- {
- actualCall = actual.next();
- if (actualCall.equals(expectedCall))
- {
- found = true;
- break actual;
- }
- }
-
- if (!found)
- {
- throw new AssertionFailedError( "The method call " +
- expectedCall +
- " was expected but has not occurred (actual calls = "+_methodCalls+")");
- }
- }
- }
- }
-
- /* ------------------------------------------------------------------------ */
- /* assertCall method
- /* ------------------------------------------------------------------------ */
- /**
- * This does the same as checkCall, but throws an
- * junit.framework.AssertionFailedError if the call did not occur.
- *
- * @param call the call that was expected
- */
- public void assertCalled(MethodCall call)
- {
- if (!checkCall(call))
- {
- throw new AssertionFailedError("The method call " +
- call +
- " was expected but has not occurred. Actual calls: " + getMethodCallsAsString());
- }
- }
-
- /* ------------------------------------------------------------------------ */
- /* assertCalledExactNumberOfTimes method
- /* ------------------------------------------------------------------------ */
- /**
- * This method asserts that the method specified in the call parameter has
- * been called the number of times specified by numberOfCalls. If
- * numberOfCalls is zero this method is equivalent to assertNotCalled.
- *
- * @param call The call that was made.
- * @param numberOfCalls The number of times the call should have been made.
- */
- public void assertCalledExactNumberOfTimes(MethodCall call, int numberOfCalls)
- {
- int callCount = 0;
-
- for (MethodCall callMade : _methodCalls)
- {
- if (callMade.equals(call))
- {
- callCount++;
- }
- }
-
- if (numberOfCalls != callCount)
- {
- throw new AssertionFailedError("The method call " + call +
- " should have been called " + numberOfCalls +
- " time(s), but was called " + callCount + " time(s)");
- }
- }
-
- /* ------------------------------------------------------------------------ */
- /* assertNotCalled method
- /* ------------------------------------------------------------------------ */
- /**
- * This method throws an junit.framework.AssertionFailedError if the specified
- * call was invoked on the skeleton.
- *
- * @param call the call to check.
- */
- public void assertNotCalled(MethodCall call)
- {
- Assert.assertFalse( "The method call " +
- call +
- "occurred in the skeleton " +
- this.toString(), checkCall(call));
- }
-
- /* ------------------------------------------------------------------------ */
- /* assertMockNotCalled method
- /* ------------------------------------------------------------------------ */
- /**
- * This method throws an junit.framework.AssertionFailedError if the skeleton
- * has had any methods invoked on it.
- */
- public void assertSkeletonNotCalled()
- {
- Assert.assertEquals("The skeleton " + this.toString() +
- " has had the following method invoked on it " + getMethodCallsAsString(),
- 0, _methodCalls.size());
- }
-
- // Instance mock creation methods
-
- /* ------------------------------------------------------------------------ */
- /* createMock method
- /* ------------------------------------------------------------------------ */
- /**
- * Creates a new Mock using this skeleton backing it.
- *
- * @param interfaceClasses an array of interface the mock should implement.
- * @return the mock
- */
- public Object createMock(Class<?> ... interfaceClasses)
- {
- ClassLoader cl;
-
- if (interfaceClasses.length > 0) cl = interfaceClasses[0].getClassLoader();
- else cl = Thread.currentThread().getContextClassLoader();
-
- return Proxy.newProxyInstance(cl, interfaceClasses, this);
- }
-
- /* ------------------------------------------------------------------------ */
- /* createMock method
- /* ------------------------------------------------------------------------ */
- /**
- * Creates a new Mock using this skeleton backing it.
- *
- * @param <T> The object type
- * @param interfaceClass an array of interface the mock should implement.
- * @return the mock
- */
- public <T> T createMock(Class<T> interfaceClass)
- {
- return interfaceClass.cast(Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class[] {interfaceClass}, this));
- }
-
- /* ------------------------------------------------------------------------ */
- /* invokeReturnTypeHandlers method
- /* ------------------------------------------------------------------------ */
- /**
- * This method invokes the return type proxy for the specified class. If a
- * ReturnTypeHandler for that type has not been registered then if the class
- * represents an interface a new mock will be returned, backed by this
- * skeleton, otherwise null will be returned.
- *
- * @param type the type to be invoked.
- * @return the returned object.
- * @throws Exception if an error occurs when invoking the return type handler.
- */
- public Object invokeReturnTypeHandlers(Class<?> type) throws Exception
- {
- if (_typeHandlers.containsKey(type))
- {
- ReturnTypeHandler rth = _typeHandlers.get(type);
- return rth.handle(type, this);
- }
- else if (type.isInterface())
- {
- return createMock(type);
- }
- else
- {
- return null;
- }
- }
-
- // Miscellaneous methods that have been deprecated and will be removed.
-
- /* ------------------------------------------------------------------------ */
- /* createReturnTypeProxy method
- /* ------------------------------------------------------------------------ */
- /**
- * create a proxy for given return type.
- *
- * @deprecated use invokeReturnTypeHandlers instead
- *
- * @param type The return type for which a handler is required
- * @return ReturnTypeHandler The return type handler
- * @throws Exception Thrown if an exception occurs.
- */
- /* ------------------------------------------------------------------------ */
- @Deprecated
- private final Object createReturnTypeProxy(Class<?> type) throws Exception
- {
- return invokeReturnTypeHandlers(type);
- }
-
- /* ------------------------------------------------------------------------ */
- /* isReadWriteProperty method
- /* ------------------------------------------------------------------------ */
- /**
- * This method returns true if the method passed a getter for a read write
- * java bean property. This is worked out by checking that a setter and getter
- * exist for the property and that the setter and getter take and return
- * exactly the same time.
- *
- * @param objClass The object the read write method has been invoked on.
- * @param method The method to be checked.
- * @return true if it is a getter or setter for a read write property.
- */
- private boolean isReadWriteProperty(Class<?> objClass, Method method)
- {
- String methodName = method.getName();
- String propertyName = methodName.substring(3);
- Class<?>[] parameters = method.getParameterTypes();
- Class<?> clazz;
- boolean result = false;
-
- if (methodName.startsWith("get") && parameters.length == 0)
- {
- clazz = method.getReturnType();
- try
- {
- objClass.getMethod("set" + propertyName, clazz);
- result = true;
- }
- catch (NoSuchMethodException e)
- {
- if (isPrimitive(clazz))
- {
- clazz = getOtherForm(clazz);
- try
- {
- objClass.getMethod("set" + propertyName, clazz);
- result = true;
- }
- catch (NoSuchMethodException e1)
- {
-
- }
- }
- }
- }
- else if (methodName.startsWith("is") && parameters.length == 0)
- {
- clazz = method.getReturnType();
- if (clazz.equals(Boolean.class) || clazz.equals(boolean.class))
- {
- propertyName = methodName.substring(2);
- try
- {
- objClass.getMethod("set" + propertyName, clazz);
- result = true;
- }
- catch (NoSuchMethodException e)
- {
-
- if (isPrimitive(clazz))
- {
- clazz = getOtherForm(clazz);
- try
- {
- objClass.getMethod("set" + propertyName, clazz);
- result = true;
- }
- catch (NoSuchMethodException e1)
- {
-
- }
- }
- }
- }
- }
- else if (methodName.startsWith("set") && parameters.length == 1)
- {
- clazz = parameters[0];
-
- try
- {
- Method getter = objClass.getMethod("get" + propertyName, new Class[0]);
- result = checkClasses(getter.getReturnType(), clazz);
- }
- catch (NoSuchMethodException e)
- {
- if (isPrimitive(clazz))
- {
- clazz = getOtherForm(clazz);
- try
- {
- Method getter = objClass.getMethod("get" + propertyName, new Class[0]);
- result = checkClasses(getter.getReturnType(), clazz);
- }
- catch (NoSuchMethodException e1)
- {
- if (clazz.equals(Boolean.class) || clazz.equals(boolean.class))
- {
- try
- {
- Method getter = objClass.getMethod("is" + propertyName, new Class[0]);
- result = checkClasses(getter.getReturnType(), clazz);
- }
- catch (NoSuchMethodException e2)
- {
- clazz = getOtherForm(clazz);
- try
- {
- Method getter = objClass.getMethod("is" + propertyName, new Class[0]);
- result = checkClasses(getter.getReturnType(), clazz);
- }
- catch (NoSuchMethodException e3)
- {
- }
- }
- }
- }
- }
- }
- }
-
- return result;
- }
-
-
- /* ------------------------------------------------------------------------ */
- /* isPrimitive method
- /* ------------------------------------------------------------------------ */
- /**
- * This method returns true if the class object represents a primitive or the
- * object version of a primitive.
- *
- * @param clazz The class to be checked.
- * @return true if it is a primitive or a wrapper.
- */
- private boolean isPrimitive(Class<?> clazz)
- {
- boolean result = false;
-
- if (clazz.isPrimitive())
- {
- result = true;
- }
- else
- {
- result = clazz.equals(Boolean.class) || clazz.equals(Byte.class) ||
- clazz.equals(Short.class) || clazz.equals(Character.class) ||
- clazz.equals(Integer.class) || clazz.equals(Long.class) ||
- clazz.equals(Float.class) || clazz.equals(Double.class);
- }
-
- return result;
- }
-
- /* ------------------------------------------------------------------------ */
- /* getOtherForm method
- /* ------------------------------------------------------------------------ */
- /**
- * This method takes a class representing either a primitive or an object
- * wrapper. If the class is a primitive type the object wrapper class is
- * returned. If the class is an object wrapper class the primitive type is
- * returned.
- *
- * @param clazz
- * @return the class representing the primitive object wrapper.
- */
- private Class<?> getOtherForm(Class<?> clazz)
- {
- Class<?> result = null;
-
- if (clazz.equals(boolean.class)) result = Boolean.class;
- else if (clazz.equals(byte.class)) result = Byte.class;
- else if (clazz.equals(short.class)) result = Short.class;
- else if (clazz.equals(char.class)) result = Character.class;
- else if (clazz.equals(int.class)) result = Integer.class;
- else if (clazz.equals(long.class)) result = Long.class;
- else if (clazz.equals(float.class)) result = Float.class;
- else if (clazz.equals(double.class)) result = Double.class;
- else if (clazz.equals(Boolean.class)) result = boolean.class;
- else if (clazz.equals(Byte.class)) result = byte.class;
- else if (clazz.equals(Short.class)) result = short.class;
- else if (clazz.equals(Character.class)) result = char.class;
- else if (clazz.equals(Integer.class)) result = int.class;
- else if (clazz.equals(Long.class)) result = long.class;
- else if (clazz.equals(Float.class)) result = float.class;
- else if (clazz.equals(Double.class)) result = double.class;
-
- return result;
- }
-
- /* ------------------------------------------------------------------------ */
- /* checkClasses method
- /* ------------------------------------------------------------------------ */
- /**
- * This method returns true if the two classes are the same or if one is
- * primitive that the other is a primitive wrapper.
- *
- * @param type1
- * @param type2
- * @return true if the classes are compatible.
- */
- private boolean checkClasses(Class<?> type1, Class<?> type2)
- {
- boolean result = false;
-
- if ((type1.isPrimitive() && type2.isPrimitive()) ||
- (!type1.isPrimitive() && !type2.isPrimitive()))
- {
- result = type1.equals(type2);
- }
- else
- {
- result = (type1.equals(boolean.class) && type2.equals(Boolean.class)) ||
- (type1.equals(byte.class) && type2.equals(Byte.class)) ||
- (type1.equals(short.class) && type2.equals(Short.class)) ||
- (type1.equals(char.class) && type2.equals(Character.class)) ||
- (type1.equals(int.class) && type2.equals(Integer.class)) ||
- (type1.equals(long.class) && type2.equals(Long.class)) ||
- (type1.equals(float.class) && type2.equals(Float.class)) ||
- (type1.equals(double.class) && type2.equals(Double.class)) ||
- (type2.equals(boolean.class) && type1.equals(Boolean.class)) ||
- (type2.equals(byte.class) && type1.equals(Byte.class)) ||
- (type2.equals(short.class) && type1.equals(Short.class)) ||
- (type2.equals(char.class) && type1.equals(Character.class)) ||
- (type2.equals(int.class) && type1.equals(Integer.class)) ||
- (type2.equals(long.class) && type1.equals(Long.class)) ||
- (type2.equals(float.class) && type1.equals(Float.class)) ||
- (type2.equals(double.class) && type1.equals(Double.class));
- }
-
- return result;
- }
-
- /* ------------------------------------------------------------------------ */
- /* getMethodCallsAsString method
- /* ------------------------------------------------------------------------ */
- /**
- * This method builds a String that contains the method calls that have been
- * made on this skeleton. It puts each call on a separate line.
- *
- * @return the string representation of the method call.
- */
- private String getMethodCallsAsString()
- {
- StringBuilder builder = new StringBuilder();
-
- for (MethodCall call : _methodCalls)
- {
- builder.append(call);
- builder.append("\r\n");
- }
-
- return builder.toString();
- }
-
-}
+/*
+ * 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 org.apache.aries.unittest.mocks;
+
+import java.lang.ref.SoftReference;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import junit.framework.Assert;
+import junit.framework.AssertionFailedError;
+
+import org.apache.aries.unittest.mocks.annotations.InjectSkeleton;
+import org.apache.aries.unittest.mocks.annotations.Singleton;
+
+/**
+ * <p>The Skeleton class is an implementation of the
+ * <code>java.lang.reflect.InvocationHandler</code> that can be used for
+ * dynamic mock objects.
+ * </p>
+ *
+ * <ol>
+ * <li>The static newMock methods can be used to create completely new mock
+ * objects backed by an entirely new skeleton.
+ * </li>
+ * <li>The static getSkeleton method can be used to obtain the skeleton
+ * backing a given mock.
+ * </li>
+ * <li>The createMock methods can be used to create a new mock object based on
+ * the skeleton that is invoked.
+ * </li>
+ * <li>The registerMethodCallHandler method can be used to register a handler
+ * that will be invoked when a method is called.
+ * </li>
+ * <li>The registerReturnTypeHandler method can be used to register a handler
+ * that will be invoked when a method with a specific return type is
+ * invoked. It should be noted that registered ReturnTypeHandlers will be
+ * invoked only if a method call handler has not been registered for the
+ * method that was invoked.
+ * </li>
+ * <li>The setReturnValue method can be used to set a value that will be
+ * returned when a method is invoked.
+ * </li>
+ * <li>The checkCalls methods can be used to determine if the methods in the
+ * list should have been called. They return a boolean to indicate if the
+ * expected calls occurred.
+ * </li>
+ * <li>The assertCalls method performs the same operation as the checkCalls,
+ * but throws an junit.framework.AssertionFailedError if the calls don't
+ * match. This intended for use within the junit test framework
+ * </li>
+ * <li>If no method call or return type handlers have been registered for a
+ * call then if the return type is an interface then a mock that implements
+ * that interface will be returned, otherwise null will be returned.
+ * </li>
+ * </ol>
+ */
+public final class Skeleton implements InvocationHandler
+{
+ /** A list of calls made on this skeleton */
+ private List<MethodCall> _methodCalls;
+ /** The invocation handler to call after MethodCall and ReturnType handlers */
+ private DefaultInvocationHandler default_Handler;
+ /** The method call handlers */
+ private Map<MethodCall, MethodCallHandler> _callHandlers;
+ /** The type handlers */
+ private Map<Class<?>, ReturnTypeHandler> _typeHandlers;
+ /** The parameter map */
+ private Map<String, Object> _mockParameters;
+ /** A Map of mock objects to Maps of properties */
+ private Map<Object, Map<String, Object>> _objectProperties;
+ /** A Map of exception notification listeners */
+ private Map<Class<?>, List<ExceptionListener>> _notificationListeners;
+ /** The template class used to create this Skeleton, may be null */
+ private Object _template;
+ /** Cached template objects */
+ private static ConcurrentMap<Object, SoftReference<Object>> _singletonMocks = new ConcurrentHashMap<Object, SoftReference<Object>>();
+
+ // Constructors
+
+ /* ------------------------------------------------------------------------ */
+ /* Skeleton constructor
+ /* ------------------------------------------------------------------------ */
+ /**
+ * constructs the skeleton with the default method call handlers and the
+ * default return type handlers.
+ */
+ private Skeleton()
+ {
+ reset();
+ }
+
+ // Static methods create methods
+
+ /* ------------------------------------------------------------------------ */
+ /* newMock method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * This method returns a completely new mock object backed by a new skeleton
+ * object. It is equivalent to
+ * <code>new Skeleton().createMock(interfaceClazzes)</code>
+ *
+ * @param interfaceClazzes the classes the mock should implement
+ * @return the new mock object.
+ */
+ public final static Object newMock(Class<?> ... interfaceClazzes)
+ {
+ return new Skeleton().createMock(interfaceClazzes);
+ }
+
+ /* ------------------------------------------------------------------------ */
+ /* newMock method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * This method returns a completely new mock object backed by a new skeleton
+ * object. It is equivalent to
+ * <code>new Skeleton().createMock(interfaceClazzes)</code>
+ *
+ * @param <T> The object type.
+ * @param interfaceClazz the classes the mock should implement
+ * @return the new mock object.
+ */
+ public final static <T> T newMock(Class<T> interfaceClazz)
+ {
+ return interfaceClazz.cast(new Skeleton().createMock(interfaceClazz));
+ }
+
+ /**
+ * It is often the case that only a subset of methods on an interface are needed, but
+ * those methods that are needed are quite complex. In this case a static mock forces
+ * you into implementing lots of methods you do not need, and produces problems when
+ * new methods are added to the interface being implemented. This method can essentially
+ * be used to complete the interface implementation. The object passed in is an instance
+ * of a class that implements a subset of the methods on the supplied interface. It does
+ * not need to implement the interface itself. The returned object will implement the full
+ * interface and delegate to the methods on the templateObject where necessary.
+ *
+ * @param <T> The object type.
+ * @param template The template object for the mock
+ * @param interfaceClazz The interface to implement
+ * @return An implementation of the interface that delegates (where appropraite) onto the template.
+ */
+ public final static <T> T newMock(final Object template, Class<T> interfaceClazz)
+ {
+ Class<?> templateClass = template.getClass();
+
+ if (templateClass.getAnnotation(Singleton.class) != null) {
+ SoftReference<Object> mock = _singletonMocks.get(template);
+ if (mock != null) {
+ Object theMock = mock.get();
+ if (theMock == null) {
+ _singletonMocks.remove(template);
+ } else if (interfaceClazz.isInstance(theMock)) {
+ return interfaceClazz.cast(theMock);
+ }
+ }
+ }
+
+ Skeleton s = new Skeleton();
+ s._template = template;
+
+ for (Method m : interfaceClazz.getMethods()) {
+ try {
+ final Method m2 = templateClass.getMethod(m.getName(), m.getParameterTypes());
+
+ MethodCall mc = new MethodCall(interfaceClazz, m.getName(), (Object[])m.getParameterTypes());
+ s.registerMethodCallHandler(mc, new MethodCallHandler()
+ {
+ public Object handle(MethodCall methodCall, Skeleton parent) throws Exception
+ {
+
+ try {
+ m2.setAccessible(true);
+ return m2.invoke(template, methodCall.getArguments());
+ } catch (InvocationTargetException ite) {
+ if(ite.getTargetException() instanceof Exception)
+ throw (Exception)ite.getTargetException();
+ else throw new Exception(ite.getTargetException());
+ }
+ }
+ });
+ } catch (NoSuchMethodException e) {
+ // do nothing here, it is a method not on the interface so ignore it.
+ }
+ }
+
+ Field[] fs = template.getClass().getFields();
+
+ for (Field f : fs) {
+ InjectSkeleton sk = f.getAnnotation(InjectSkeleton.class);
+
+ if (sk != null) {
+ f.setAccessible(true);
+ try {
+ f.set(template, s);
+ } catch (IllegalArgumentException e) {
+ e.printStackTrace();
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ Object o = s.createMock(interfaceClazz);
+ _singletonMocks.put(template, new SoftReference<Object>(o) {
+ @Override
+ public boolean enqueue()
+ {
+ _singletonMocks.remove(template);
+
+ System.out.println("Done cleanup");
+
+ return super.enqueue();
+ }
+ });
+ return interfaceClazz.cast(o);
+ }
+
+ // static mock query methods
+
+ /* ------------------------------------------------------------------------ */
+ /* getSkeleton method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * This method returns the Skeleton backing the supplied mock object. If the
+ * supplied object is not a mock an IllegalArgumentException will be thrown.
+ *
+ * @param mock the mock object
+ * @return the skeleton backing the mock object
+ * @throws IllegalArgumentException thrown if the object is not a mock.
+ */
+ public final static Skeleton getSkeleton(Object mock)
+ throws IllegalArgumentException
+ {
+ InvocationHandler ih = Proxy.getInvocationHandler(mock);
+ if (ih instanceof Skeleton)
+ {
+ return (Skeleton)ih;
+ }
+ throw new IllegalArgumentException("The supplied proxy (" + mock + ") was not a (Jetstream mediations) dynamic mock ");
+ }
+
+ /* ------------------------------------------------------------------------ */
+ /* isSkeleton method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * This method returns true if and only the provided object is backed by a
+ * Skeleton. Another way to think about this is if it returns true then a
+ * call to getSkeleton will not result in an IllegalArgumentException, and is
+ * guaranteed to return a Skeleton.
+ *
+ * @param mock the mock to test.
+ * @return true if it is backed by a skeleton.
+ */
+ public final static boolean isSkeleton(Object mock)
+ {
+ if (Proxy.isProxyClass(mock.getClass())) {
+ InvocationHandler ih = Proxy.getInvocationHandler(mock);
+
+ return (ih instanceof Skeleton);
+ }
+
+ return false;
+ }
+
+ // InvocationHandler defined methods.
+
+ /* ------------------------------------------------------------------------ */
+ /* invoke method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * This method is invoked by the mock objects. It constructs a MethodCall
+ * object representing the call and adds it to the list of calls that were
+ * made. (It should be noted that if the method is toString, hashCode or
+ * equals then they are not added to the list.) It then calls a registered
+ * MethodCallHandler, if a MethodCallHandler is not registered then a
+ * ReturnTypeHandler is invoked. If a ReturnTypeHandler is not invoked then
+ * the registered default InvocationHandler is called. By default the Skeleton
+ * is constructed with a DefaultInvocationHandler. If the invoked method has
+ * an interface as a return type then the DefaultInvocationHandler will return
+ * a new mock implementing that interface. If the return type is a class null
+ * will be returned.
+ *
+ * @param targetObject The mock object that was invoked.
+ * @param calledMethod The method that was called.
+ * @param arguments The arguments that were passed.
+ * @return The return of the method invoked.
+ * @throws Throwable Any exception thrown.
+ */
+ public Object invoke(Object targetObject, Method calledMethod, Object[] arguments)
+ throws Throwable
+ {
+ String methodName = calledMethod.getName();
+ MethodCall call = new MethodCall(targetObject, methodName, arguments);
+
+ if (!DefaultMethodCallHandlers.isDefaultMethodCall(call))
+ {
+ _methodCalls.add(call);
+ }
+
+ Object result;
+
+ try
+ {
+ if (_callHandlers.containsKey(call))
+ {
+ MethodCallHandler handler = _callHandlers.get(call);
+ result = handler.handle(call, this);
+ }
+ else if (isReadWriteProperty(targetObject.getClass(), calledMethod))
+ {
+ String propertyName = methodName.substring(3);
+ if (methodName.startsWith("get") || methodName.startsWith("is"))
+ {
+ if (methodName.startsWith("is")) propertyName = methodName.substring(2);
+
+ Map<String, Object> properties = _objectProperties.get(targetObject);
+ if (properties == null)
+ {
+ properties = new HashMap<String, Object>();
+ _objectProperties.put(targetObject, properties);
+ }
+
+ if (properties.containsKey(propertyName))
+ {
+ result = properties.get(propertyName);
+ }
+ else if (_typeHandlers.containsKey(calledMethod.getReturnType()))
+ {
+ result = createReturnTypeProxy(calledMethod.getReturnType());
+ }
+ else
+ {
+ result = default_Handler.invoke(targetObject, calledMethod, arguments);
+ }
+ }
+ // Must be a setter.
+ else
+ {
+ Map<String, Object> properties = _objectProperties.get(targetObject);
+ if (properties == null)
+ {
+ properties = new HashMap<String, Object>();
+ _objectProperties.put(targetObject, properties);
+ }
+
+ properties.put(propertyName, arguments[0]);
+ result = null;
+ }
+ }
+ else if (_typeHandlers.containsKey(calledMethod.getReturnType()))
+ {
+ result = createReturnTypeProxy(calledMethod.getReturnType());
+ }
+ else
+ {
+ result = default_Handler.invoke(targetObject, calledMethod, arguments);
+ }
+ }
+ catch (Throwable t)
+ {
+ Class<?> throwableType = t.getClass();
+ List<ExceptionListener> listeners = _notificationListeners.get(throwableType);
+ if (listeners != null)
+ {
+ for (ExceptionListener listener : listeners)
+ {
+ listener.exceptionNotification(t);
+ }
+ }
+
+ throw t;
+ }
+
+ return result;
+ }
+
+ // MethodCall registration methods
+
+ /* ------------------------------------------------------------------------ */
+ /* registerMethodCallHandler method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * This method registers a MethodCallHandler for the specified MethodCall.
+ *
+ * @param call The method that was called.
+ * @param handler The MethodCallHandler.
+ */
+ public void registerMethodCallHandler(MethodCall call, MethodCallHandler handler)
+ {
+ deRegisterMethodCallHandler(call);
+ _callHandlers.put(call, handler);
+ }
+
+ /* ------------------------------------------------------------------------ */
+ /* deRegisterMethodCallHandler method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * This method removes a registered MethodCallHandler for the specified
+ * MethodCall.
+ *
+ * @param call the specified MethodCall
+ */
+ public void deRegisterMethodCallHandler(MethodCall call)
+ {
+ _callHandlers.remove(call);
+ }
+
+ /* ------------------------------------------------------------------------ */
+ /* reset method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * This method resets the skeleton to the state it was in prior just after it
+ * was constructed.
+ */
+ public void reset()
+ {
+ _methodCalls = new LinkedList<MethodCall>();
+ _callHandlers = new HashMap<MethodCall, MethodCallHandler>();
+ _typeHandlers = new HashMap<Class<?>, ReturnTypeHandler>();
+ DefaultReturnTypeHandlers.registerDefaultHandlers(this);
+ DefaultMethodCallHandlers.registerDefaultHandlers(this);
+ default_Handler = new DefaultInvocationHandler(this);
+ _mockParameters = new HashMap<String, Object>();
+ _objectProperties = new HashMap<Object, Map<String, Object>>();
+ _notificationListeners = new HashMap<Class<?>, List<ExceptionListener>>();
+ }
+
+ /* ------------------------------------------------------------------------ */
+ /* clearMethodCalls method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * This method clears the method calls list for the skeleton
+ */
+ public void clearMethodCalls()
+ {
+ _methodCalls = new LinkedList<MethodCall>();
+ }
+
+
+ /* ------------------------------------------------------------------------ */
+ /* setReturnValue method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * This is a convenience method for registering a method call handler where
+ * a specific value should be returned when a method is called, rather than
+ * some logic needs to be applied. The value should be an object or the object
+ * version of the primitive for the methods return type, so if the method
+ * returns short the value must be an instance of java.lang.Short, not
+ * java.lang.Integer.
+ *
+ * @param call the method being called.
+ * @param value the value to be returned when that method is called.
+ */
+ public void setReturnValue(MethodCall call, final Object value)
+ {
+ Class<?> clazz;
+ try {
+ clazz = Class.forName(call.getClassName());
+ } catch (ClassNotFoundException e) {
+ throw new IllegalStateException("This should be impossible as we have already seen this class loaded");
+ }
+
+
+ Method[] methods = clazz.getMethods();
+
+ methods: for (Method m : methods) {
+ if(!!!m.getName().equals(call.getMethodName()))
+ continue methods;
+
+ Object[] args = call.getArguments();
+ Class<?>[] parms = m.getParameterTypes();
+
+ if (args.length == parms.length) {
+ for (int i = 0; i < args.length; i++) {
+ if (args[i] instanceof Class && args[i].equals(parms[i])) {
+ } else if (parms[i].isInstance(args[i])) {
+ } else {
+ continue methods;
+ }
+ }
+
+ Class<?> returnType = m.getReturnType();
+ if (returnType.isPrimitive()) {
+ if (returnType == boolean.class) returnType = Boolean.class;
+ else if (returnType == byte.class) returnType = Byte.class;
+ else if (returnType == short.class) returnType = Short.class;
+ else if (returnType == char.class) returnType = Character.class;
+ else if (returnType == int.class) returnType = Integer.class;
+ else if (returnType == long.class) returnType = Long.class;
+ else if (returnType == float.class) returnType = Float.class;
+ else if (returnType == double.class) returnType = Double.class;
+ }
+
+ if (value != null && !!!returnType.isInstance(value)) {
+ throw new IllegalArgumentException("The object cannot be returned by the requested method: " + call);
+ } else break methods;
+ }
+ }
+
+
+
+ registerMethodCallHandler(call, new MethodCallHandler()
+ {
+ public Object handle(MethodCall methodCall, Skeleton parent) throws Exception
+ {
+ return value;
+ }
+ });
+ }
+
+ /* ------------------------------------------------------------------------ */
+ /* setThrow method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * This is a convenience method for registering a method call handler where
+ * a specific exception should be thrown when the method is called, rather
+ * than some logic needs to be applied.
+ *
+ * @param call the method being called
+ * @param thingToThrow the exception to throw.
+ */
+ public void setThrows(MethodCall call, final Exception thingToThrow)
+ {
+ registerMethodCallHandler(call, new MethodCallHandler()
+ {
+ public Object handle(MethodCall methodCall, Skeleton parent) throws Exception
+ {
+ thingToThrow.fillInStackTrace();
+ throw thingToThrow;
+ }
+ });
+ }
+
+ /* ------------------------------------------------------------------------ */
+ /* setThrow method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * This is a convenience method for registering a method call handler where
+ * a specific exception should be thrown when the method is called, rather
+ * than some logic needs to be applied.
+ *
+ * @param call the method being called
+ * @param thingToThrow the exception to throw.
+ */
+ public void setThrows(MethodCall call, final Error thingToThrow)
+ {
+ registerMethodCallHandler(call, new MethodCallHandler()
+ {
+ public Object handle(MethodCall methodCall, Skeleton parent) throws Exception
+ {
+ thingToThrow.fillInStackTrace();
+ throw thingToThrow;
+ }
+ });
+ }
+
+ // ReturnType registration methods
+
+ /* ------------------------------------------------------------------------ */
+ /* registerReturnTypeHandler method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * This method registers a ReturnTypeHandler for the specified class.
+ *
+ * @param clazz The class to be handled.
+ * @param handler The ReturnTypeHandler
+ */
+ public void registerReturnTypeHandler(Class<?> clazz, ReturnTypeHandler handler)
+ {
+ deRegisterReturnTypeHandler(clazz);
+ _typeHandlers.put(clazz, handler);
+ }
+
+ /* ------------------------------------------------------------------------ */
+ /* deRegisterReturnTypeHandler method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * This method removes a registration for a ReturnTypeHandler for the
+ * specified class.
+ *
+ * @param clazz The class to deregister the handler for.
+ */
+ public void deRegisterReturnTypeHandler(Class<?> clazz)
+ {
+ _typeHandlers.remove(clazz);
+ }
+
+ // Exception notification methods
+
+ /* ------------------------------------------------------------------------ */
+ /* registerExceptionListener method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * This method registers an ExceptionListener when the specified Exception is
+ * thrown.
+ *
+ * @param throwableType The type of the Throwable
+ * @param listener The listener.
+ */
+ public void registerExceptionListener(Class<?> throwableType, ExceptionListener listener)
+ {
+ List<ExceptionListener> l = _notificationListeners.get(throwableType);
+ if (l == null)
+ {
+ l = new ArrayList<ExceptionListener>();
+ _notificationListeners.put(throwableType, l);
+ }
+ l.add(listener);
+ }
+
+ // parameter related methods
+
+ /* ------------------------------------------------------------------------ */
+ /* setParameter method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * This method allows a parameter to be set. It is intended to be used by
+ * MethodCallHandlers and ReturnTypeHandlers.
+ *
+ * @param key The key
+ * @param value The value
+ */
+ public void setParameter(String key, Object value)
+ {
+ _mockParameters.put(key, value);
+ }
+
+ /* ------------------------------------------------------------------------ */
+ /* getParameter method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * This method allows a parameter to be retrieved.
+ *
+ * @param key the key the parameter was set using
+ * @return the parameter
+ */
+ public Object getParameter(String key)
+ {
+ return _mockParameters.get(key);
+ }
+
+ /* ------------------------------------------------------------------------ */
+ /* getTemplateObject method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * @return the template object, if one was used when initializing this skeleton.
+ */
+ public Object getTemplateObject()
+ {
+ return _template;
+ }
+
+ // default InvocationHandler related methods
+
+ /**
+ * @param defaultHandler The defaultHandler to set.
+ */
+ public void setDefaultHandler(DefaultInvocationHandler defaultHandler)
+ {
+ this.default_Handler = defaultHandler;
+ }
+
+ // MethodCall list check methods
+
+ /* ------------------------------------------------------------------------ */
+ /* checkCalls method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * This method checks that the calls in the list occurred. If the addCalls
+ * boolean is true then their must be an exact match. If the allCalls boolean
+ * is false then the calls in the list must occur in that order, but other
+ * calls can be in between.
+ *
+ * @param calls The expected calls list
+ * @param allCalls true if an exact match comparison should be performed
+ * @return true if they the expected calls match.
+ */
+ public boolean checkCalls(List<MethodCall> calls, boolean allCalls)
+ {
+ boolean found = false;
+ if (allCalls)
+ {
+ return calls.equals(_methodCalls);
+ }
+ else
+ {
+ Iterator<MethodCall> actual = _methodCalls.iterator();
+ for (MethodCall expectedCall : calls)
+ {
+ found = false;
+ actual: while (actual.hasNext())
+ {
+ MethodCall actualCall = actual.next();
+ if (actualCall.equals(expectedCall))
+ {
+ found = true;
+ break actual;
+ }
+ }
+ }
+ }
+
+ return found;
+ }
+
+ /* ------------------------------------------------------------------------ */
+ /* checkCall method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * Checks that the specified method has been called on this skeleton
+ *
+ * @param call the call that should have been called.
+ * @return true if the MethodCall occurs in the list.
+ */
+ public boolean checkCall(MethodCall call)
+ {
+ return this._methodCalls.contains(call);
+ }
+
+ // MethodCall list assert methods
+
+ /* ------------------------------------------------------------------------ */
+ /* assertCalls method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * This method checks that the MethodCalls objects in the given list were
+ * made and throws an AssertionFailedError if they were not. If allCalls is
+ * true the given list and the calls list must be identical. If allCalls is
+ * false other calls could have been made on the skeleton in between ones
+ * specified in the list.
+ *
+ * @param calls the list of calls
+ * @param allCalls whether an exact match between the lists is required
+ * @throws AssertionFailedError if a failure has occurred.
+ */
+ public void assertCalled(List<MethodCall> calls, boolean allCalls) throws AssertionFailedError
+ {
+ if (allCalls)
+ {
+ if ((calls == null) && (_methodCalls == null)) return;
+
+ if (calls == null)
+ {
+ throw new AssertionFailedError("expected null, but was " + _methodCalls);
+ }
+
+ if (_methodCalls == null)
+ {
+ throw new AssertionFailedError("expected:" + calls + " but was null");
+ }
+
+ if (calls.equals(_methodCalls)) return;
+
+ // OK compare lists and decide on differences - initially all the lists are different
+ int startOfDifferences = 0;
+ // Remove the common start of sequence
+ boolean lastItemSame = true;
+
+ for (int i = 0; i < calls.size() && i < _methodCalls.size() && lastItemSame; i++)
+ {
+ if ((calls.get(i) == null) && (_methodCalls.get(i) == null))
+ {
+ lastItemSame = true;
+ }
+ else if ((calls.get(i) == null) || (_methodCalls.get(i) == null))
+ {
+ lastItemSame = false;
+ }
+ else
+ {
+ lastItemSame = calls.get(i).equals(_methodCalls.get(i));
+ }
+
+ if (lastItemSame) startOfDifferences++;
+
+ }//for
+ // Now remove the common bit at the end
+ int endOfDifferencesInExpected = calls.size();
+ int endOfDifferencesInReceived = _methodCalls.size();
+ lastItemSame = true;
+
+ while ((endOfDifferencesInExpected > startOfDifferences)
+ && (endOfDifferencesInReceived > startOfDifferences)
+ && lastItemSame)
+ {
+ int ap = endOfDifferencesInExpected - 1;
+ int bp = endOfDifferencesInReceived - 1;
+
+ if ((calls.get(ap) == null) && (_methodCalls.get(bp) == null))
+ {
+ lastItemSame = true;
+ }
+ else if ((calls.get(ap) == null) || (_methodCalls.get(bp) == null))
+ {
+ lastItemSame = false;
+ }
+ else
+ {
+ lastItemSame = calls.get(ap).equals(_methodCalls.get(bp));
+ }
+
+ if (lastItemSame)
+ {
+ endOfDifferencesInExpected--;
+ endOfDifferencesInReceived--;
+ }
+
+ }//while
+
+ String failureText;
+ // OK, now build the failureText
+ if (endOfDifferencesInExpected == startOfDifferences)
+ {
+ failureText =
+ "Expected calls and actual calls differed because "
+ + _methodCalls.subList(startOfDifferences, endOfDifferencesInReceived)
+ + " inserted after element "
+ + startOfDifferences;
+
+ }
+ else if (endOfDifferencesInReceived == startOfDifferences)
+ {
+ failureText =
+ "Expected calls and actual calls differed because "
+ + calls.subList(startOfDifferences, endOfDifferencesInExpected)
+ + " missing after element "
+ + startOfDifferences;
+
+ }
+ else
+ {
+ if ((endOfDifferencesInExpected == startOfDifferences + 1)
+ && (endOfDifferencesInReceived == startOfDifferences + 1))
+ {
+
+ failureText =
+ "Expected calls and actual calls differed because element "
+ + startOfDifferences
+ + " is different (calls:"
+ + calls.get(startOfDifferences)
+ + " but was:"+_methodCalls.get(startOfDifferences)+") ";
+
+ }
+ else if (endOfDifferencesInExpected == startOfDifferences + 1)
+ {
+
+ failureText =
+ "Expected calls and actual calls differed because element "
+ + startOfDifferences
+ + " ("
+ + calls.get(startOfDifferences)
+ + ") has been replaced by "
+ + _methodCalls.subList(startOfDifferences, endOfDifferencesInReceived);
+ }
+ else
+ {
+ failureText =
+ "Expected calls and actual calls differed because elements between "
+ + startOfDifferences
+ + " and "
+ + (endOfDifferencesInExpected - 1)
+ + " are different (expected:"
+ + calls.subList(startOfDifferences, endOfDifferencesInExpected)
+ + " but was:"
+ + _methodCalls.subList(startOfDifferences, endOfDifferencesInReceived)
+ + ")";
+ }//if
+ }//if
+
+ throw new AssertionFailedError(failureText + " expected:" + calls + " but was:" + _methodCalls);
+ }
+ else
+ {
+ Iterator<MethodCall> expected = calls.iterator();
+ Iterator<MethodCall> actual = _methodCalls.iterator();
+ while (expected.hasNext())
+ {
+ boolean found = false;
+ MethodCall expectedCall = expected.next();
+ MethodCall actualCall = null;
+
+ actual: while (actual.hasNext())
+ {
+ actualCall = actual.next();
+ if (actualCall.equals(expectedCall))
+ {
+ found = true;
+ break actual;
+ }
+ }
+
+ if (!found)
+ {
+ throw new AssertionFailedError( "The method call " +
+ expectedCall +
+ " was expected but has not occurred (actual calls = "+_methodCalls+")");
+ }
+ }
+ }
+ }
+
+ /* ------------------------------------------------------------------------ */
+ /* assertCall method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * This does the same as checkCall, but throws an
+ * junit.framework.AssertionFailedError if the call did not occur.
+ *
+ * @param call the call that was expected
+ */
+ public void assertCalled(MethodCall call)
+ {
+ if (!checkCall(call))
+ {
+ throw new AssertionFailedError("The method call " +
+ call +
+ " was expected but has not occurred. Actual calls: " + getMethodCallsAsString());
+ }
+ }
+
+ /* ------------------------------------------------------------------------ */
+ /* assertCalledExactNumberOfTimes method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * This method asserts that the method specified in the call parameter has
+ * been called the number of times specified by numberOfCalls. If
+ * numberOfCalls is zero this method is equivalent to assertNotCalled.
+ *
+ * @param call The call that was made.
+ * @param numberOfCalls The number of times the call should have been made.
+ */
+ public void assertCalledExactNumberOfTimes(MethodCall call, int numberOfCalls)
+ {
+ int callCount = 0;
+
+ for (MethodCall callMade : _methodCalls)
+ {
+ if (callMade.equals(call))
+ {
+ callCount++;
+ }
+ }
+
+ if (numberOfCalls != callCount)
+ {
+ throw new AssertionFailedError("The method call " + call +
+ " should have been called " + numberOfCalls +
+ " time(s), but was called " + callCount + " time(s)");
+ }
+ }
+
+ /* ------------------------------------------------------------------------ */
+ /* assertNotCalled method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * This method throws an junit.framework.AssertionFailedError if the specified
+ * call was invoked on the skeleton.
+ *
+ * @param call the call to check.
+ */
+ public void assertNotCalled(MethodCall call)
+ {
+ Assert.assertFalse( "The method call " +
+ call +
[... 363 lines stripped ...]