/*
 * BaseAssociatorImpl.java
 *
 * Generated by {productTitle}  {versionName} - {utilityTitle}
 *
 * {copywrite}
 */
package {packageName}.common;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;

import javax.cim.CIMClass;
import javax.cim.CIMDataType;
import javax.cim.CIMInstance;
import javax.cim.CIMObjectPath;
import javax.cim.CIMProperty;
import javax.wbem.CloseableIterator;
import javax.wbem.WBEMException;
import javax.wbem.WBEMOperation;
import javax.wbem.WBEMOperationErrors;

import com.ws.wbem.CloseableAddableIterator;

import com.ws.wbem.query.fql.FQLParser;

/**
 * This class implements a very basic mechanism for doing associations.<BR>
 * It is recommended that sub-classes override these functions and provide a
 * more efficient mechanism for performing the operations. Since this code does
 * not know the model of the classes being used it must use inefficient
 * algorithms to obtain its results. Additionally the mechanism provided here
 * will not work properly with associations that use the same classes as the end
 * points (e.g. CIM_StorageSynchronized).
 */
public abstract class BaseAssociatorImpl extends BaseProviderImpl implements
AssocProvImplIF {

	// The name of one of the classes defined in a binary association
	final private BaseProviderImpl mReference1;
	// The name of the class for the other class defined in a binary association
	final private BaseProviderImpl mReference2;
	// The role that reference1 plays (e.g. property name)
	final private String mRef1Role;
	// The role that reference2 plays (e.g. property name)
	final private String mRef2Role;
	// Indicates if this class is an association between the same class or not
	final boolean mReflective;

	/**
	 * Association constructor
	 *
	 * @param pClzName
	 *            The CIM name of this class
	 * @param pReference1
	 *            A reference to first endpoints implementation object
	 * @param pReference1Role
	 *            The role (property) the first endpoint plays in this
	 *            association
	 * @param pReference2
	 *            A reference to second endpoints implementation object
	 * @param pReference2Role
	 *            The role (property) the second endpoint plays in this
	 *            association
	 */
	public BaseAssociatorImpl(String pClzName, BaseProviderImpl pReference1,
			String pReference1Role, BaseProviderImpl pReference2,
			String pReference2Role) {
		super(pClzName);
		// If any of these are null then it is a programming error
		if (null == pReference1) {
			throw new NullPointerException("Reference1 impl is null");
		}
		this.mReference1 = pReference1;

		if (null == pReference2) {
			throw new NullPointerException("Reference2 impl is null");
		}
		this.mReference2 = pReference2;

		mReflective = getReference1Name().equalsIgnoreCase(getReference2Name());

		if (null == pReference1Role) {
			throw new NullPointerException("Reference1 role is null");
		}
		this.mRef1Role = pReference1Role;

		if (null == pReference2Role) {
			throw new NullPointerException("Reference2 role is null");
		}
		this.mRef2Role = pReference2Role;
	}

	public void assocEnumerate(
			final CloseableAddableIterator<CIMInstance> iter,
			final CIMObjectPath assocName, final CIMObjectPath objectName,
			final String resultClass, final String role,
			final String resultRole, final String[] propertyList,
			FQLParser fqlParser, final boolean continueOnError,
			final WBEMOperation wbemOperation) throws WBEMException {
		try {
			// ensure this call is for this association
			if (!getClassName().equalsIgnoreCase(assocName.getObjectName())) {
				return;
			}

			// This function needs to return one side of the association, get
			// all instances of this associations then we will iterate through
			// them to find a match
			final CloseableAddableIterator<CIMInstance> instIter = getAllInstancesThreaded(
					objectName, null, fqlParser, wbemOperation);

			// If the role or resultRole is specified we can treat this as a
			// non-reflective as we know which end point to return
			if (null != role || null != resultRole || !isReflective()) {
				doNonReflectiveAssoc(true, iter, objectName, role, resultRole,
						resultClass, propertyList, instIter,
						wbemOperation);
			} else {
				// No role or result role was provided, since this is a
				// reflective association we need to examine both endpoints
				// and determine what to return to the client
				doReflectiveAssoc(true, iter, objectName, resultClass,
						propertyList, instIter, wbemOperation);
			}
		} catch (final WBEMException we) {
			iter.setException(we);
		} catch (final Throwable t) {
			iter.setException(WBEMOperationErrors.getOtherFailureException(objectName,
					t, wbemOperation, objectName.getObjectName()));
		} finally {
			if (null != iter && !iter.isDone()) {
				iter.done();
			}
		}
	}

	/**
	 * Used by default implementation of getAllInstances for association classes
	 *
	 * @param iter
	 *            Iterator to convert
	 * @return A list containing the elements
	 */
	protected List<CIMInstance> convertIterator2List(
			CloseableAddableIterator<CIMInstance> iter) {
		final List<CIMInstance> ret = new ArrayList<CIMInstance>();
		while (iter.hasNext()) {
			final CIMInstance inst = iter.next();
			ret.add(inst);
		}
		return ret;
	}

	/**
	 * Helper function - this function takes 2 CIMObjectPaths and places them
	 * into an new ObjectPath representing the association object
	 *
	 * @param op
	 *            CIMObjectPath passed in from the server
	 * @param ref1
	 *            A CIMObjectpath representing the first object in this
	 *            association
	 * @param ref2
	 *            A CIMObjectpath representing the second object in this
	 *            association
	 * @return A CIMObjectPath which has the passed in paths as properties
	 * @throws WBEMException
	 */
	final public CIMObjectPath createAssocObjectPath(CIMObjectPath op,
			CIMObjectPath ref1, CIMObjectPath ref2) throws WBEMException {
		// validate parameters
		if (null == ref1
				|| !ref1.getObjectName().equalsIgnoreCase(getReference1Name())) {
			throw new WBEMException(
					"Was expecting 'ref1' to be an Objectpath for "
							+ getReference1Name() + " but got "
							+ (null == ref1 ? "null" : ref1.getObjectName()));
		}

		if (null == ref2
				|| !ref2.getObjectName().equalsIgnoreCase(getReference2Name())) {
			throw new WBEMException(
					"Was expecting 'ref2' to be an Objectpath for "
							+ getReference2Name() + " but got "
							+ (null == ref2 ? "null" : ref2.getObjectName()));
		}

		// create key properties
		final CIMProperty<?>[] keys = new CIMProperty<?>[] {
				new CIMProperty<CIMObjectPath>(getReference1Role(),
						new CIMDataType(getReference1Name()), ref1),
						new CIMProperty<CIMObjectPath>(getReference2Role(),
								new CIMDataType(getReference2Name()), ref2) };

		return new CIMObjectPath(op.getScheme(), op.getHost(), op.getPort(),
				op.getNamespace(), getClassName(), keys, op.getXmlSchemaName());

	}

	/**
	 * Performs an association operation on a class in which the endpoints are
	 * <b>not</b> the same class
	 *
	 * @param assoctorCall
	 *            Indicates if the function is being called from an assoctiator
	 *            or references function
	 * @param resultIter
	 *            The iterator in which the results are sent to
	 * @param objectName
	 *            The objectpath we need to get the association for
	 * @param role
	 *            The role, can be null
	 * @param resultRole
	 *            the resultRole, can be null
	 * @param resultClass
	 *            the resultClass, can be null
	 * @param propertyList
	 *            If returning instances, which properties should be returned
	 * @param instIter
	 *            An iterator that will contain all the instances of <b>THIS</b>
	 *            association
	 * @param wbemOperation
	 *            The name of the operation (e.g. Associators)
	 *
	 * @throws WBEMException
	 */
	private void doNonReflectiveAssoc(boolean associatorCall,
			final CloseableAddableIterator<CIMInstance> resultIter,
			final CIMObjectPath objectName, final String role,
			final String resultRole, final String resultClass,
			final String[] propertyList,
			CloseableAddableIterator<CIMInstance> instIter,
			final WBEMOperation wbemOperation) throws WBEMException {
		// Determine that role and result class are valid
		final String theRole = getRole(objectName, role);
		// if theRole is null, the specified objectName is not an endpoint for
		// this association class. Do nothing
		if (null != theRole) {
			final String theRsltRole = getResultRole(theRole);
			final String theRsltClass = getResultClass(objectName, resultRole,
					resultClass);
			// Validate that the resultClass is valid. If theRsltClass is null,
			// we don't return any results
			if (null != theRsltClass) {
				// iterate the results
				while (instIter.hasNext()) {
					final CIMInstance inst = instIter.next();
					// now get the property with the name contained by 'theRole'
					final CIMObjectPath op = (CIMObjectPath) inst
							.getPropertyValue(theRole);
					if (null != op) {
						// found property, see if the property matches what was
						// passed
						// in. If they match then we need to return the other end
						if (objectName.equalsModelPath(op)) {
							// make sure result class is correct type
							final CIMObjectPath retOp = (CIMObjectPath) inst
									.getPropertyValue(theRsltRole);
							if (retOp.getObjectName()
									.equalsIgnoreCase(theRsltClass)) {
								// Return type is correct, return item
								returnElement(resultIter, associatorCall, retOp,
										inst, propertyList);
							}
						}
					}
				}
			}
		}
	}

	/**
	 * This function is called when we do not know the result role and the
	 * association is reflective (e.g. both endpoints are the same class).
	 *
	 * @param associatorCall
	 *            Indicates we are doing an Associator or a References call
	 * @param resultIter
	 *            Where the results are sent to
	 * @param objectName
	 *            The objectpath for which this function was called for
	 * @param resultClass
	 *            The name of the class which will be returned, maybe null
	 * @param propertyList
	 *            An array indicating which properties should be returned, null
	 *            indicates all properties
	 * @param instIter
	 *            An iterator which will contain all of the instances of this
	 *            association
	 * @param wbemOperation
	 *            The name of the operation (e.g. Associators)
	 * @throws WBEMException
	 */
	private void doReflectiveAssoc(boolean associatorCall,
			CloseableAddableIterator<CIMInstance> resultIter,
			CIMObjectPath objectName, String resultClass,
			String[] propertyList,
			CloseableAddableIterator<CIMInstance> instIter,
			final WBEMOperation wbemOperation) throws WBEMException {
		// Validate that the resultClass is valid. If getResultClass() is null,
		// we don't return any results
		if (null != getResultClass(objectName, null, resultClass)) {

			// iterate the results
			while (instIter.hasNext()) {
				// Get the association instance
				final CIMInstance inst = instIter.next();
				// Since we do not know the role or resultrole (this function will
				// not be called if we have that information) then we need to check
				// both endpoints of the association to see if it matches.
				for (int x = 0; x < 2; x++) {
					final String propName = x == 0 ? getReference1Role()
							: getReference2Role();
					final CIMObjectPath op = (CIMObjectPath) inst
							.getPropertyValue(propName);
					if (null != op && op.equals(objectName)) {
						// we found a match, return element
						returnElement(resultIter, associatorCall, op, inst,
								propertyList);
					}
				}
			}
		}
	}

	/**
	 * Generates the instance of class the provider instrumenting
	 *
	 * @param objectPath
	 *            CIMObjectPath that contains the information (host, scheme.
	 *            port, namespace) used to create the CIMObjectpath of the
	 *            returned instance
	 * @param opRef1
	 *            CIMObjectPath for the first reference property value
	 * @param opRef2
	 *            CIMObjectPath for the second reference property value
	 * @param propertyList
	 *            property list used to filter out the returned properties
	 * @param FQLParser
	 *            FQLParser object used to determine if instance will be
	 *            returned. Can be null
	 * @return If FQLParser is not null and the instance doesn't evaluate to the
	 *         FQL query represented by the FQLParser object, null is returned.
	 *         Otherwise, the CIMInstance representing an association class
	 * @throws WBEMException
	 */
	public CIMInstance generateAssocInstance(final CIMObjectPath objectPath,
			final CIMObjectPath opRef1, final CIMObjectPath opRef2,
			String[] propertyList, FQLParser fqlParser) throws WBEMException {
		final CIMProperty<?>[] keys = new CIMProperty<?>[2];
		keys[0] = new CIMProperty<CIMObjectPath>(getReference1Role(),
				new CIMDataType(getReference1Name()), opRef1, true);
		keys[1] = new CIMProperty<CIMObjectPath>(getReference2Role(),
				new CIMDataType(getReference2Name()), opRef2, true);

		final CIMObjectPath opRet = new CIMObjectPath(objectPath.getScheme(),
				objectPath.getHost(), objectPath.getPort(),
				objectPath.getNamespace(), getClassName(), keys);

		CIMInstance instRet = new CIMInstance(opRet, keys);
		if (null == fqlParser || fqlParser.evaluate(instRet)) {
			instRet = instRet.filterProperties(propertyList);
		} else {
			// did not pass FQLParser.evaluate() method, set return instance
			// to null
			instRet = null;
		}
		return instRet;
	}

	@Override
	public void getAllInstances(CloseableAddableIterator<CIMInstance> iter,
			CIMObjectPath op, String[] propertyList, final FQLParser fqlParser,
			WBEMOperation wbemOperation) throws WBEMException {

		try {

			// iterators used to hold instances for each endpoint
			final CloseableAddableIterator<CIMInstance> iterRef1 = new CloseableAddableIterator<CIMInstance>();
			final CloseableAddableIterator<CIMInstance> iterRef2 = new CloseableAddableIterator<CIMInstance>();

			final CIMObjectPath opRef1 = new CIMObjectPath(op.getScheme(),
					op.getHost(), op.getPort(), op.getNamespace(),
					getReference1Name(), null);
			final CIMObjectPath opRef2 = new CIMObjectPath(op.getScheme(),
					op.getHost(), op.getPort(), op.getNamespace(),
					getReference2Name(), null);

			// use endpoint implementations to get all the instances of each
			// endpoint
			getReference1().getAllInstances(iterRef1, opRef1, null, null,
					wbemOperation);
			getReference2().getAllInstances(iterRef2, opRef2, null, null,
					wbemOperation);

			// Convert iterRef2 to a list because we may need to iterate through
			// it multiple times
			final List<CIMInstance> listRef2 = convertIterator2List(iterRef2);

			// create new instances where all instances of Ref1 class
			// are associated to all instances of Ref2 class and add
			// these instances to the return iterator
			while (!iterRef1.isClosed() && iterRef1.hasNext()) {
				final CIMInstance instRef1 = iterRef1.next();
				for (final CIMInstance instRef2 : listRef2) {

					// generate instance using 2 reference objectpaths
					final CIMInstance inst = generateAssocInstance(op,
							instRef1.getObjectPath(), instRef2.getObjectPath(),
							propertyList, fqlParser);
					if (null != inst) {
						iter.add(inst);
					}

				}
			}

		} catch (final Throwable t) {
			iter.setException(WBEMOperationErrors.getOtherFailureException(op,
					t, wbemOperation, op.getObjectName()));
		} finally {
			if (null != iter && !iter.isClosed()) {
				iter.done();
			}
		}
	}

	/**
	 * Helper function, creates a thread and returns an iterator. The thread
	 * will get all of the instances of this association object
	 *
	 * @param objectName
	 *            The objectpath of the class that is calling this operation
	 * @param includeClassOrigin
	 *            Should class origin information be returned
	 * @param propertyList
	 *            If null, all properties will be returned otherwise just those
	 *            properties in the list will be returned
	 * @param FQLParser
	 *            FQLParser object used to determine if instance will be
	 *            returned. Can be null
	 * @param wbemOperation
	 *            Name of the operation
	 * @return An iterator which the caller can use to process the returned
	 *         instances
	 */
	private CloseableAddableIterator<CIMInstance> getAllInstancesThreaded(
			final CIMObjectPath objectName, final String[] propertyList,
			final FQLParser fqlParser, final WBEMOperation wbemOperation) {
		final CloseableAddableIterator<CIMInstance> instIter = new CloseableAddableIterator<CIMInstance>();
		final Runnable runner = new Runnable() {
			@Override
			public void run() {
				try {
					getAllInstances(instIter, objectName, propertyList,
							fqlParser, wbemOperation);
				} catch (final WBEMException e) {
					if (null == instIter.getWBEMException()) {
						instIter.setException(e);
					}
				} finally {
					if (null != instIter && !instIter.isDone()) {
						instIter.done();
					}
				}
			}
		};
		// create a thread to get all of the instances, this allows us to
		// asynchronously process the results
		final Thread t = new Thread(runner);
		t.setDaemon(true);
		t.start();

		return instIter;
	}

	/**
	 * Gets the implementation for the first part of the association, this would
	 * typically be the 'Antecedent' or 'GroupComponent' etc...
	 *
	 * @return The implementation object of the first end point
	 */
	public BaseProviderImpl getReference1() {
		return mReference1;
	}

	/**
	 * Gets the class name of the first endpoint
	 *
	 * @return A string representing the name of the class
	 */
	public String getReference1Name() {
		return getReference1().getClassName();
	}

	/**
	 * This tells us the role that Association1 plays in this association, for
	 * example it maybe 'Antecedent' or 'GroupComponent' etc...
	 *
	 * @return A string with the role this object plays in the association
	 */
	public String getReference1Role() {
		return mRef1Role;
	}

	/**
	 * Gets the implementation for the first part of the association, this would
	 * typically be the 'Dependent' or 'PartComponent' etc...
	 *
	 * @return The implementation object of the second end point
	 */
	public BaseProviderImpl getReference2() {
		return mReference2;
	}

	/**
	 * Gets the class name of the second endpoint
	 *
	 * @return A string representing the name of the class
	 */
	public String getReference2Name() {
		return getReference2().getClassName();
	}

	/**
	 * This tells us the role that Association2 plays in this association, for
	 * example it maybe 'Dependent' or 'PartComponent' etc...
	 *
	 * @return A string with the role this object plays in the association
	 */
	public String getReference2Role() {
		return mRef2Role;
	}

	/**
	 * Helper function used to try and determine the class name of what objects
	 * we will be returning.<BR>
	 * This function also validates that the information (if provided) is valid,
	 * e.g. if the resultClass is passed in, this function ensures that it is
	 * correct)
	 *
	 * @param objectName
	 *            An objectpath representing the object that is being operated
	 *            on
	 * @param resultRole
	 *            The role 'objectname' plays in the association, can be null
	 * @param resultClass
	 *            The name of the class which will be returned, can be null
	 * @return A string representing the name of the class that will be
	 *         returned. Can return null if specified class isn't a reference
	 *         class for this association
	 * @throws WBEMException
	 */
	private String getResultClass(final CIMObjectPath objectName,
			final String resultRole, final String resultClass)
					throws WBEMException {
		String ret = null;
		// Determine which endpoint was sent from the client
		if (getReference1Name().equalsIgnoreCase(objectName.getObjectName())) {
			// if no resultRole was passed in use the role we know it is, if the
			// result role was passed in then verify it matches what the role is
			if (null == resultRole
					|| getReference2Role().equalsIgnoreCase(resultRole)) {
				// now check if the passed in result class matches or ignore
				// if it was not passed in just return the class name
				if (null == resultClass
						|| getReference2Name().equalsIgnoreCase(resultClass)) {
					ret = getReference2Name();
				} else {
					// Specified result class is not null and not equal to the
					// ref2 class. Need to see if ref2 class is a subclass of
					// the specified resultClass.
					// Enumerate the subclasses of the specified result class
					// and check if ref2 class is equal to one of them.
					final CIMObjectPath opResultClass = new CIMObjectPath(
							objectName.getScheme(), objectName.getHost(),
							objectName.getPort(), objectName.getNamespace(),
							resultClass, null);
					try {
						final CloseableIterator<CIMClass> iterClasses = getWBEMServerHandle()
								.enumerateClasses(opResultClass, true, false,
										false);
						while (iterClasses.hasNext()) {
							if (getReference2Name().equalsIgnoreCase(
									iterClasses.next().getName())) {
								// ref2Class is subclass of specified class.
								// set return value to ref2 classname and
								// stop looking
								ret = getReference2Name();
								break;
							}
						}
					} catch (final WBEMException we) {
						final Object[] objs = { getReference2Name(),
								resultClass, we };
						mLogger.log(
								Level.WARNING,
								"Caught exception trying to determine if class "
										+ "{0} is a subclass of class {1}:\n{2}",
								objs);
					}

				}
			}
		} else if (getReference2Name().equalsIgnoreCase(
				objectName.getObjectName())) {
			// if no role was passed in use the role we know it is, if the
			// role was passed in then verify it matches what the role is.
			// Since we are dealing with the
			if (null == resultRole
					|| getReference1Role().equalsIgnoreCase(resultRole)) {
				// now check if the passed in result class matches or ignore
				// if it was not passed in just return the class name
				if (null == resultClass
						|| getReference1Name().equalsIgnoreCase(resultClass)) {
					ret = getReference1Name();
				} else {
					// Specified result class is not null and not equal to the
					// ref1 class. Need to see if ref1 class is a subclass of
					// the specified resultClass.
					// Enumerate the subclasses of the specified result class
					// and check if ref1 class is equal to one of them.
					final CIMObjectPath opResultClass = new CIMObjectPath(
							objectName.getScheme(), objectName.getHost(),
							objectName.getPort(), objectName.getNamespace(),
							resultClass, null);
					try {
						final CloseableIterator<CIMClass> iterClasses = getWBEMServerHandle()
								.enumerateClasses(opResultClass, true, false,
										false);
						while (iterClasses.hasNext()) {
							final String classname = iterClasses.next()
									.getName();
							if (getReference1Name().equalsIgnoreCase(classname)) {
								// ref1Class is subclass of specified class.
								// set return value to ref1 classname and
								// stop looking
								ret = getReference1Name();
								break;
							}
						}
					} catch (final WBEMException we) {
						final Object[] objs = { getReference1Name(),
								resultClass, we };
						mLogger.log(
								Level.WARNING,
								"Caught exception trying to determine if class "
										+ "{0} is a subclass of class {1}:\n{2}",
										objs);
					}
				}
			}
		}
		return ret;
	}

	/*
	 * Given a role this will return the result role
	 */
	private String getResultRole(String theRole) {
		String ret = null;
		final String r1Role = getReference1Role();
		final String r2Role = getReference2Role();
		// Determine which endpoint was sent from the client
		if (r1Role.equalsIgnoreCase(theRole)) {
			ret = r2Role;
		} else {
			ret = r1Role;
		}
		return ret;
	}

	/**
	 * Helper function that helps to determine the role the passed in objectpath
	 * plays in the association.<BR>
	 * If role is passed in, this function validates that it is correct.
	 *
	 * @param objectName
	 *            The objectpath for which this operation is being executed for
	 * @param role
	 *            The role 'objectName' plays in association, can be null
	 * @return A string representing the name of the role that 'objectName'
	 *         plays in the association. Can return null.
	 * @throws WBEMException
	 */
	private String getRole(final CIMObjectPath objectName, final String role)
			throws WBEMException {
		String ret = null;
		// Determine which endpoint was sent from the client
		if (getReference1Name().equalsIgnoreCase(objectName.getObjectName())) {
			// if no role was passed in use the role we know it is, if the
			// role was passed in then verify it matches what the role is
			if (null == role || getReference1Role().equalsIgnoreCase(role)) {
				ret = getReference1Role();
			}
		} else if (getReference2Name().equalsIgnoreCase(
				objectName.getObjectName())) {
			// if no role was passed in use the role we know it is, if the
			// role was passed in then verify it matches what the role is
			if (null == role || getReference2Role().equalsIgnoreCase(role)) {
				ret = getReference2Role();
			}
		}
		return ret;
	}

	/**
	 * Tells us if the association is one in which the endpoints are the same
	 * class.
	 *
	 * @return true if the endpoints are the same class, false otherwise.
	 */
	public boolean isReflective() {
		return mReflective;
	}

	public void refEnumerate(final CloseableAddableIterator<CIMInstance> iter,
			final CIMObjectPath assocName, final CIMObjectPath objectName,
			final String role, final String[] propertyList,
			final FQLParser fqlParser, final boolean continueOnError,
			final WBEMOperation wbemOperation) throws WBEMException {
		try {
			// ensure this call is for this association
			if (!getClassName().equalsIgnoreCase(assocName.getObjectName())) {
				return;
			}

			// This function needs to return association objects, get
			// all instances of this associations then we will iterate through
			// them to find a match
			final CloseableAddableIterator<CIMInstance> instIter = getAllInstancesThreaded(
					objectName, null, fqlParser, wbemOperation);

			// If the role or resultRole is specified we can treat this as a
			// non-reflective as we know which end point to return
			if (null != role || !isReflective()) {
				doNonReflectiveAssoc(false, iter, objectName, role, null, null,
						propertyList, instIter, wbemOperation);
			} else {
				// No role or result role was provided, since this is a
				// reflective association we need to examine both endpoints
				// and determine what to return to the client
				doReflectiveAssoc(false, iter, objectName, null, propertyList,
						instIter, wbemOperation);
			}
		} catch (final WBEMException we) {
			iter.setException(we);
		} catch (final Throwable t) {
			iter.setException(WBEMOperationErrors.getOtherFailureException(objectName,
					t, wbemOperation, objectName.getObjectName()));
		} finally {
			if (null != iter && !iter.isDone()) {
				iter.done();
			}
		}
	}

	/**
	 * Helper method used to return an objectpath or instance
	 *
	 * @param resultIter
	 *            Where the element should be returned to
	 * @param associatorCall
	 *            True if this is an associator call, false for references
	 * @param op
	 *            A CIMObjectPath of the object for which this operation is
	 *            acting on
	 * @param inst
	 *            A CIMInstance of the class being returned to the caller
	 * @param propertyList
	 *            If null all properties are returned, if non-null then only
	 *            those properties listed in the array will be returned
	 * @throws WBEMException
	 */
	private void returnElement(
			CloseableAddableIterator<CIMInstance> resultIter,
			boolean associatorCall, CIMObjectPath op, CIMInstance inst,
			String[] propertyList) throws WBEMException {

		if (associatorCall) {
			final BaseProviderImpl t;
			// Determine which endpoint we are returning
			if (getReference1Name().equalsIgnoreCase(op.getObjectName())) {
				t = getReference1();
			} else {
				t = getReference2();
			}
			// Call the endpoint to get the instance we need
			final CIMInstance retInst = t.getInstance(op, propertyList);
			resultIter.add(retInst);
		} else {
			resultIter.add(inst.filterProperties(propertyList));
		}
	}
}