001package com.identityworksllc.iiq.common; 002 003import org.apache.commons.logging.Log; 004import org.apache.commons.logging.LogFactory; 005import sailpoint.tools.CloseableIterator; 006 007import java.util.Optional; 008import java.util.Stack; 009 010/** 011 * A container that will auto-close its contents when it is itself closed. 012 * Closeable items are stored in a {@link Stack} and will be closed in the 013 * reverse order of being added. Errors will be logged and ignored. 014 * 015 * Items implementing {@link AutoCloseable} (most of them) and items implementing 016 * {@link CloseableIterator} are stored separately. 017 * 018 * Example in Beanshell (with dummy methods): 019 * 020 * ``` 021 * try { 022 * Connection dbConnection = closer.add(getDatabaseConnection()); 023 * 024 * PreparedStatement stmt = closer.add(dbConnection.prepareStatement("select id from spt_identity where name = ?")); 025 * stmt.setString(1, someName); 026 * 027 * ResultSet results = closer.add(stmt.executeQuery()); 028 * while(results.next()) { 029 * // do stuff 030 * } 031 * } finally { 032 * // All three objects are closed here, in reverse order 033 * closer.close(); 034 * } 035 * ``` 036 * 037 * @see AutoCloseable 038 */ 039public class Closer implements AutoCloseable { 040 /** 041 * The logger 042 */ 043 private static final Log log = LogFactory.getLog(Closer.class); 044 045 /** 046 * A list of items that will be auto-closed, in the reverse of the 047 * order in which they were added 048 */ 049 private final Stack<AutoCloseable> autocloseList; 050 051 private final Stack<CloseableIterator<?>> closeableIteratorList; 052 053 /** 054 * Creates a new Closer container with an empty set of items 055 */ 056 public Closer() { 057 this.autocloseList = new Stack<>(); 058 this.closeableIteratorList = new Stack<>(); 059 } 060 061 /** 062 * Creates a new Closer containing the given item 063 * @param cl1 The item to add 064 */ 065 public Closer(AutoCloseable cl1) { 066 this(); 067 add(cl1); 068 } 069 070 /** 071 * Creates a new Closer containing the given items 072 * @param cl1 The item to add 073 * @param cl2 The item to add 074 */ 075 public Closer(AutoCloseable cl1, AutoCloseable cl2) { 076 this(cl1); 077 add(cl2); 078 } 079 080 /** 081 * Creates a new Closer containing the given items 082 * @param cl1 The item to add 083 * @param cl2 The item to add 084 * @param cl3 The item to add 085 */ 086 public Closer(AutoCloseable cl1, AutoCloseable cl2, AutoCloseable cl3) { 087 this(cl1, cl2); 088 add(cl3); 089 } 090 091 /** 092 * Creates a new Closer containing the given items 093 * @param cl1 The item to add 094 * @param cl2 The item to add 095 * @param cl3 The item to add 096 * @param cl4 The item to add 097 */ 098 public Closer(AutoCloseable cl1, AutoCloseable cl2, AutoCloseable cl3, AutoCloseable cl4) { 099 this(cl1, cl2, cl3); 100 add(cl4); 101 } 102 103 /** 104 * Adds the {@link AutoCloseable} item to the stack to be closed when this container is closed 105 * @param ac The auto-closeable item to add to the stack 106 * @return The passed object, allowing you to open and add in the same line of code 107 * @param <T> Some object extending AutoCloseable 108 */ 109 public <T extends AutoCloseable> T add(T ac) { 110 this.autocloseList.push(ac); 111 return ac; 112 } 113 114 /** 115 * Adds the {@link CloseableIterator} item to the stack to be closed when this container is closed 116 * @param ci The CloseableIterator item to add to the stack 117 * @return The passed object, allowing you to open and add in the same line of code 118 */ 119 public CloseableIterator<?> add(CloseableIterator<?> ci) { 120 this.closeableIteratorList.push(ci); 121 return ci; 122 } 123 124 /** 125 * Pops each item off the stack and calls close() on it. Any errors will be logged 126 * and ignored. The list will be empty after this method is finished, allowing those 127 * objects to be garbage-collected. 128 * 129 * @throws Exception If anything uncatchable fails 130 */ 131 @Override 132 public void close() throws Exception { 133 while(!autocloseList.isEmpty()) { 134 AutoCloseable ac = autocloseList.pop(); 135 try { 136 ac.close(); 137 } catch(Exception e) { 138 log.error("Unable to close item of type " + ac.getClass(), e); 139 } 140 } 141 142 while(!closeableIteratorList.isEmpty()) { 143 CloseableIterator<?> ci = closeableIteratorList.pop(); 144 try { 145 ci.close(); 146 } catch(Exception e) { 147 log.error("Unable to close item of type " + ci.getClass(), e); 148 } 149 } 150 } 151 152 /** 153 * Extracts the managed object of the given type. For example, assuming that you 154 * passed a {@link java.sql.Connection} into a {@link Closer}: 155 * 156 * ``` 157 * Connection conn = closer.get(Connection.class); 158 * ``` 159 * @param requestedType The type of the requested class 160 * @return An optional containing the object, if one matches 161 * @param <T> The output object 162 */ 163 public <T extends AutoCloseable> Optional<T> get(Class<T> requestedType) { 164 for(AutoCloseable ac : this.autocloseList) { 165 if (requestedType.isInstance(ac)) { 166 return Optional.of(requestedType.cast(ac)); 167 } 168 } 169 170 return Optional.empty(); 171 } 172}