001package com.identityworksllc.iiq.common; 002 003import org.apache.commons.logging.Log; 004import org.apache.commons.logging.LogFactory; 005import sailpoint.api.SailPointContext; 006import sailpoint.object.QueryOptions; 007import sailpoint.object.SailPointObject; 008import sailpoint.tools.GeneralException; 009import sailpoint.tools.Util; 010 011import java.io.Closeable; 012import java.sql.Connection; 013import java.util.Collections; 014import java.util.Iterator; 015import java.util.List; 016 017/** 018 * Utilities to quietly (i.e. without throwing an exception) invoke certain common 019 * operations. This prevents the need to write try/catch boilerplate. In most cases, the 020 * methods will return a sensible default (either null or an empty value) when 021 * the operation fails. 022 * 023 * Exceptions will be logged at info level. 024 */ 025public class Quietly { 026 /** 027 * Logger for this class 028 */ 029 private static final Log logger = LogFactory.getLog(Quietly.class); 030 031 /** 032 * Closes the thing quietly 033 * @param thing The thing to close 034 */ 035 public static void close(Closeable thing) { 036 invoke(thing, Closeable::close); 037 } 038 039 /** 040 * Closes the thing quietly 041 * @param thing The thing to close 042 */ 043 public static void close(AutoCloseable thing) { 044 invoke(thing, AutoCloseable::close); 045 } 046 047 /** 048 * Quietly gets a Sailpoint object 049 * 050 * If the idOrName parameter is null, or if the retrieval throws an exception, 051 * this method returns null. This is in addition to the cases where getObject() 052 * already returns null, such as if the object doesn't exist. 053 * 054 * @param context The IIQ context to use for retrieval 055 * @param objectType The SailPointObject class to retrieve 056 * @param idOrName The ID or name of the object 057 * @param <SpoType> On failures 058 * @return The object, or null in the situations described above 059 * 060 * @see SailPointContext#getObject(Class, String) 061 */ 062 public static <SpoType extends SailPointObject> SpoType getObject(SailPointContext context, Class<SpoType> objectType, String idOrName) { 063 if (Util.isNotNullOrEmpty(idOrName)) { 064 try { 065 return context.getObject(objectType, idOrName); 066 } catch (GeneralException e) { 067 logger.info("Caught an exception in Quietly.getObject", e); 068 } 069 } 070 return null; 071 } 072 073 /** 074 * Quietly gets a Sailpoint object 075 * 076 * If the idOrName parameter is null, or if the retrieval throws an exception, 077 * this method returns null. This is in addition to the cases where getObjectById() 078 * already returns null, such as if the object doesn't exist. 079 * 080 * @param context The IIQ context to use for retrieval 081 * @param objectType The SailPointObject class to retrieve 082 * @param id The ID of the object 083 * @param <SpoType> On failures 084 * @return The object, or null in the situations described above 085 * 086 * @see SailPointContext#getObjectById(Class, String) 087 */ 088 public static <SpoType extends SailPointObject> SpoType getObjectById(SailPointContext context, Class<SpoType> objectType, String id) { 089 if (Util.isNotNullOrEmpty(id)) { 090 try { 091 return context.getObjectById(objectType, id); 092 } catch (GeneralException e) { 093 logger.info("Caught an exception in Quietly.getObjectById", e); 094 } 095 } 096 return null; 097 } 098 099 /** 100 * Quietly gets a Sailpoint object 101 * 102 * If the idOrName parameter is null, or if the retrieval throws an exception, 103 * this method returns null. This is in addition to the cases where getObjectByName() 104 * already returns null, such as if the object doesn't exist. 105 * 106 * @param context The IIQ context to use for retrieval 107 * @param objectType The SailPointObject class to retrieve 108 * @param name The ID of the object 109 * @param <SpoType> On failures 110 * @return The object, or null in the situations described above 111 * 112 * @see SailPointContext#getObjectByName(Class, String) 113 */ 114 public static <SpoType extends SailPointObject> SpoType getObjectByName(SailPointContext context, Class<SpoType> objectType, String name) { 115 if (Util.isNotNullOrEmpty(name)) { 116 try { 117 return context.getObjectByName(objectType, name); 118 } catch (GeneralException e) { 119 logger.info("Caught an exception in Quietly.getObjectByName", e); 120 } 121 } 122 return null; 123 } 124 125 /** 126 * Performs the action, logging and swallowing any exceptions 127 * @param input The input object 128 * @param action The action to perform on the input object 129 * @param <T> The type of the input object 130 */ 131 public static <T> void invoke(T input, Functions.ConsumerWithError<? super T> action) { 132 try { 133 action.acceptWithError(input); 134 } catch(Throwable t) { 135 logger.error("Caught an exception in Quietly.invoke", t); 136 if (t instanceof Error) { 137 throw (Error)t; 138 } 139 } 140 } 141 142 /** 143 * Performs the action, logging and swallowing any exceptions 144 * @param input The input object 145 * @param input2 The second input object 146 * @param action The action to perform on the input object 147 * @param <In> The type of the input object 148 */ 149 public static <In, In2, Out> Out invokeWithOutput(In input, In2 input2, Functions.BiFunctionWithError<? super In, ? super In2, Out> action) { 150 try { 151 return action.applyWithError(input, input2); 152 } catch(Throwable t) { 153 logger.error("Caught an exception in Quietly.invokeWithOutput", t); 154 if (t instanceof Error) { 155 throw (Error)t; 156 } 157 } 158 return null; 159 } 160 161 /** 162 * Performs the action, logging and swallowing any exceptions. 163 * 164 * @param input The input object 165 * @param action The action to perform on the input object 166 * @param <In> The type of the input object 167 * @param <Out> The type of the output object 168 * 169 * @return Either the result of the computation, if it was error-free, or null 170 */ 171 public static <In, Out> Out invokeWithOutput(In input, Functions.FunctionWithError<? super In, ? extends Out> action) { 172 try { 173 return action.applyWithError(input); 174 } catch(Throwable t) { 175 logger.error("Caught an exception in Quietly.invokeWithOutput", t); 176 if (t instanceof Error) { 177 throw (Error)t; 178 } 179 } 180 return null; 181 } 182 183 /** 184 * Rolls back the connection quietly 185 * @param connection The conection to roll back 186 */ 187 public static void rollback(Connection connection) { 188 invoke(connection, Connection::rollback); 189 } 190 191 /** 192 * Quietly searches for and returns an iterator of Sailpoint objects. If this 193 * set is expected to be large, you should use IncrementalObjectIterator instead 194 * and just deal with the exceptions. 195 * 196 * All exceptions on search will be swallowed. 197 * 198 * This method never returns null, but may return an empty iterator. 199 * 200 * @param context The IIQ context 201 * @param objectType The IIQ object type to search 202 * @param qo The QueryOptions containing the search criteria 203 * @param <SpoType> The SPO type 204 * @return An Iterator containing the search results 205 * 206 * @see SailPointContext#search(Class, QueryOptions) 207 * @see sailpoint.api.IncrementalObjectIterator 208 */ 209 public static <SpoType extends SailPointObject> Iterator<SpoType> search(SailPointContext context, Class<SpoType> objectType, QueryOptions qo) { 210 try { 211 Iterator<SpoType> iterator = context.search(objectType, qo); 212 if (iterator != null) { 213 return iterator; 214 } 215 } catch(GeneralException e) { 216 logger.info("Caught an exception in Quietly.search", e); 217 } 218 return Collections.emptyIterator(); 219 } 220 221 /** 222 * Quietly searches for and returns an iterator of Object[] projections. If this 223 * set is expected to be large, you should use IncrementalProjectorIterator instead 224 * and just deal with the exceptions. 225 * 226 * All exceptions on search will be swallowed. 227 * 228 * This method never returns null, but may return an empty iterator. 229 * 230 * @param context The IIQ context 231 * @param objectType The IIQ object type to search 232 * @param qo The QueryOptions containing the search criteria 233 * @param columns The columns to return in the projection 234 * @param <SpoType> The SPO type 235 * @return An Iterator containing the search results 236 * 237 * @see SailPointContext#search(Class, QueryOptions, List) 238 * @see sailpoint.api.IncrementalProjectionIterator 239 */ 240 public static <SpoType extends SailPointObject> Iterator<Object[]> search(SailPointContext context, Class<SpoType> objectType, QueryOptions qo, List<String> columns) { 241 try { 242 Iterator<Object[]> iterator = context.search(objectType, qo, columns); 243 if (iterator != null) { 244 return iterator; 245 } 246 } catch(GeneralException e) { 247 logger.info("Caught an exception in Quietly.search", e); 248 } 249 return Collections.emptyIterator(); 250 } 251 252}