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}