001package com.identityworksllc.iiq.common.task; 002 003import com.identityworksllc.iiq.common.iterators.TransformingIterator; 004import com.identityworksllc.iiq.common.task.BasicObjectRetriever.RetrievalType; 005import sailpoint.api.SailPointContext; 006import sailpoint.object.Attributes; 007import sailpoint.object.ResourceObject; 008import sailpoint.tools.CloseableIterator; 009import sailpoint.tools.GeneralException; 010 011import java.util.Iterator; 012import java.util.Map; 013 014/** 015 * A more advanced {@link AbstractThreadedTask} that implements a "do this to these" 016 * pattern. The task will retrieve its list of items via a rule, script, flat file, or 017 * search filter. Subclasses are still responsible for implementing the 018 * {@link #threadExecute(SailPointContext, Map, Object)} method. 019 * 020 * A 'retrievalType' must be specified that is one of rule, script, sql, file, connector, 021 * provided, or filter, along with the appropriate arguments for that type of retrieval. 022 * 023 * The other arguments are the ones expected by {@link BasicObjectRetriever}. See that class 024 * for details. 025 * 026 * If the output of your retrieval is not the type you want passed to your threadExecute, you must 027 * override {@link #convertObject(Object)} and return the appropriately translated object. This class 028 * assumes that the output of convertObject is the correct type. Returning a value of the incorrect 029 * type will result in ClassCastExceptions. 030 * 031 * If you only want to support a subset of outputs, you may override {@link #supportsRetrievalType(RetrievalType)} 032 * and return false for those you do not want to support. 033 * 034 * Subclasses must invoke super.parseArgs() if they override it to extract their own inputs. 035 * 036 * @param <ItemType> the type of object being iterated over 037 */ 038public abstract class AbstractThreadedObjectIteratorTask<ItemType> extends AbstractThreadedTask<ItemType> { 039 /** 040 * Proxy to convert a Closeable iterator into an iterator 041 */ 042 public static class CloseableIteratorWrapper implements Iterator<ResourceObject> { 043 044 private final CloseableIterator<ResourceObject> closeableIterator; 045 046 public CloseableIteratorWrapper(CloseableIterator<ResourceObject> closeableIterator) { 047 this.closeableIterator = closeableIterator; 048 } 049 050 @Override 051 public boolean hasNext() { 052 return closeableIterator.hasNext(); 053 } 054 055 @Override 056 public ResourceObject next() { 057 return closeableIterator.next(); 058 } 059 } 060 061 /** 062 * Wraps the output iterators for transformation purposes. 063 * This is how {@link #convertObject(Object)} gets called. 064 */ 065 public class ResultTransformingIterator extends TransformingIterator<Object, ItemType> { 066 067 /** 068 * Constructor 069 * 070 * @param input The iterator being wrapped by this transformer 071 */ 072 public ResultTransformingIterator(Iterator<?> input) { 073 super(input, AbstractThreadedObjectIteratorTask.this::convertObject); 074 } 075 } 076 077 /** 078 * The ObjectRetriever to use to get the inputs 079 */ 080 private ObjectRetriever<ItemType> retriever; 081 082 /** 083 * Converts the input to the expected type T. By default, this just 084 * returns the input as-is. If you know what you're doing, you can leave 085 * this implementation intact, but you probably want to convert things. 086 * 087 * If the result is null, the object will be ignored. 088 * 089 * @param input The input 090 * @return The output object 091 */ 092 @SuppressWarnings("unchecked") 093 protected ItemType convertObject(Object input) { 094 return (ItemType) input; 095 } 096 097 /** 098 * Gets the iterator over the given object type 099 * @param context Sailpoint context 100 * @param args The task arguments 101 * @return The iterator of items retrieved from whatever source 102 * @throws GeneralException if any failures occur 103 */ 104 protected Iterator<ItemType> getObjectIterator(SailPointContext context, Attributes<String, Object> args) throws GeneralException { 105 retriever.setTerminationRegistrar(this::addTerminationHandler); 106 return retriever.getObjectIterator(context, args); 107 } 108 109 /** 110 * Gets the ObjectRetriever associated with this task execution 111 * 112 * @param context The context of the object retriever 113 * @param args The arguments to the task 114 * @return The object retriever 115 * @throws GeneralException if any failure occurs 116 */ 117 protected ObjectRetriever<ItemType> getObjectRetriever(SailPointContext context, Attributes<String, Object> args) throws GeneralException { 118 return new BasicObjectRetriever<>(context, args, ResultTransformingIterator::new, taskResult); 119 } 120 121 /** 122 * Termination indicator to be used by the subclasses 123 * @return True if this task has been terminated 124 */ 125 protected final boolean isTerminated() { 126 return terminated.get(); 127 } 128 129 /** 130 * Parses the task arguments, determining the retrieval type and its arguments 131 * @param args The task arguments 132 * @throws Exception if any failures occur parsing the arguments 133 */ 134 protected void parseArgs(Attributes<String, Object> args) throws Exception { 135 super.parseArgs(args); 136 137 this.retriever = getObjectRetriever(context, args); 138 139 if (retriever instanceof BasicObjectRetriever) { 140 RetrievalType retrievalType = ((BasicObjectRetriever<ItemType>)retriever).getRetrievalType(); 141 142 if (!supportsRetrievalType(retrievalType)) { 143 throw new IllegalArgumentException("This task does not support retrieval type " + retrievalType); 144 } 145 } 146 } 147 148 /** 149 * This method should return false if this task executor does not want to support the 150 * given retrieval type. 151 * 152 * @param type The type of the retrieval 153 */ 154 protected boolean supportsRetrievalType(RetrievalType type) { 155 return true; 156 } 157 158}