001package com.identityworksllc.iiq.common; 002 003import org.apache.commons.logging.Log; 004import org.apache.commons.logging.LogFactory; 005import sailpoint.tools.CloseableIterator; 006 007/** 008 * An interface to implement the "try with resources" type of structure 009 * in Beanshell, which does not support it. You should create an anonymous 010 * inner class extending {@link BSHResourceCloser}, then pass it to {@link #execute(BSHResourceCloser)}. 011 * 012 * The open() method will be called and should produce an AutoCloseable 013 * resource. After that, the run() method will be invoked with the 014 * opened resource. If you need more than one thing to be opened, you 015 * may want to return a {@link Closer}. 016 * 017 * If your resource is AutoCloseable, its close() method will be invoked. 018 * If not, you must implement close() in your class. 019 * 020 * You may 021 * 022 * Example: 023 * 024 * ``` 025 * BSHResourceCloser.execute(new BSHResourceCloser() { 026 * public Object open() { 027 * // Open a connection or something 028 * } 029 * }); 030 * 031 * @param <T> The resource type 032 * @param <U> The return type 033 */ 034public abstract class BSHResourceCloser<T, U> { 035 036 /** 037 * Logger 038 */ 039 private static final Log log = LogFactory.getLog(BSHResourceCloser.class); 040 041 /** 042 * Executes the given Beanshell method with the resource, closing it once 043 * the method has completed. 044 * 045 * @param resource The resource to use 046 * @param bshThis The 'this' reference in your Beanshell script 047 * @param methodName The method name to invoke with the resource 048 * @param <T> The type of the resource 049 * @return The result of the Beanshell method 050 * @throws Exception if anything fails 051 */ 052 public static <T> Object execute(T resource, bsh.This bshThis, String methodName) throws Exception { 053 BSHResourceCloser<T, Object> closer = new BSHResourceCloser<T, Object>() { 054 @Override 055 public T open() throws Exception { 056 return resource; 057 } 058 059 @Override 060 public Object run(T rin) throws Exception { 061 Object[] inputs = new Object[1]; 062 inputs[0] = rin; 063 return bshThis.invokeMethod(methodName, inputs); 064 } 065 }; 066 067 return execute(closer); 068 } 069 070 /** 071 * Executes your action after getting the resource required 072 * 073 * @param closer The resource closer 074 * @param <T> The resource type 075 * @param <U> The return type 076 * @return The return from your run() method 077 * @throws Exception if the resource open or the call fails 078 */ 079 public static <T, U> U execute(BSHResourceCloser<T, U> closer) throws Exception { 080 T resource = closer.open(); 081 try { 082 return closer.run(resource); 083 } finally { 084 try { 085 if (resource instanceof AutoCloseable) { 086 ((AutoCloseable) resource).close(); 087 } else if (resource instanceof CloseableIterator) { 088 ((CloseableIterator<?>) resource).close(); 089 } else { 090 closer.close(resource); 091 } 092 } catch(Exception e) { 093 log.error("Error closing resource", e); 094 } 095 } 096 } 097 098 /** 099 * Closes the given resource. This will be invoked if the resource does 100 * not implement AutoCloseable. 101 * 102 * @param resource the resource to close 103 */ 104 public void close(T resource) { 105 /* Nothing by default */ 106 log.warn("Default implementation BSHResourceCloser.close() invoked. This means that the resource is not AutoCloseable and you must implement close() yourself."); 107 } 108 109 /** 110 * Opens the resource for this action 111 * @return The resource 112 * @throws Exception if anything fails 113 */ 114 public abstract T open() throws Exception; 115 116 /** 117 * Executes the action, passing the resource returned from open() 118 * @param resource The resource to use 119 * @return An arbitrary return value 120 * @throws Exception If the action fails for some reason 121 */ 122 public abstract U run(T resource) throws Exception; 123}