001package com.identityworksllc.iiq.common.connector;
002
003import openconnector.ConnectorServices;
004import openconnector.ConnectorStateChangeListener;
005import org.apache.commons.logging.Log;
006import org.apache.commons.logging.LogFactory;
007import sailpoint.connector.AbstractConnector;
008import sailpoint.connector.AuthenticationFailedException;
009import sailpoint.connector.Connector;
010import sailpoint.connector.ConnectorException;
011import sailpoint.connector.ExpiredPasswordException;
012import sailpoint.connector.InvalidConfigurationException;
013import sailpoint.connector.ObjectNotFoundException;
014import sailpoint.object.Application;
015import sailpoint.object.AttributeDefinition;
016import sailpoint.object.Filter;
017import sailpoint.object.Partition;
018import sailpoint.object.ProvisioningPlan;
019import sailpoint.object.ProvisioningResult;
020import sailpoint.object.ResourceObject;
021import sailpoint.object.Schema;
022import sailpoint.plugin.PluginsUtil;
023import sailpoint.tools.CloseableIterator;
024import sailpoint.tools.GeneralException;
025import sailpoint.tools.Util;
026
027import java.util.HashMap;
028import java.util.List;
029import java.util.Map;
030
031/**
032 * A straightforward "delegating" connector that will allow loading of connector
033 * classes from a plugin. All connector operations will be forwarded to the
034 * delegate.
035 */
036public class BaseDelegatingConnector extends AbstractConnector implements ConnectorStateChangeListener {
037    /**
038     * The real connector to which the operation is being delegated
039     */
040    private Connector _realConnector;
041
042    /**
043     * The log
044     */
045    private final Log log;
046
047    /**
048     * Constructs a new BaseDelegatingConnector of the given type
049     * @param application The application to use for the connector
050     */
051    public BaseDelegatingConnector(Application application) {
052        super(application, null);
053        log = LogFactory.getLog(this.getClass());
054    }
055
056    @Override
057    public ResourceObject authenticate(String s, String s1) throws ConnectorException, ObjectNotFoundException, AuthenticationFailedException, ExpiredPasswordException {
058        return getRealConnector().authenticate(s, s1);
059    }
060
061    @Override
062    public ResourceObject authenticate(String s, Map<String, Object> map) throws ConnectorException, ObjectNotFoundException, AuthenticationFailedException, ExpiredPasswordException {
063        return getRealConnector().authenticate(s, map);
064    }
065
066    @Override
067    public ProvisioningResult checkStatus(String s) throws ConnectorException, GeneralException {
068        return getRealConnector().checkStatus(s);
069    }
070
071    @Override
072    public void destroy(Map<String, Object> map) throws ConnectorException {
073        getRealConnector().destroy(map);
074    }
075
076    @Override
077    public Map<String, Object> discoverApplicationAttributes(Map<String, Object> map) throws ConnectorException {
078        return getRealConnector().discoverApplicationAttributes(map);
079    }
080
081    @Override
082    public Schema discoverSchema(String s, Map<String, Object> map) throws ConnectorException {
083        return getRealConnector().discoverSchema(s, map);
084    }
085
086    @Override
087    public Map doHealthCheck(Map<String, Object> map) throws ConnectorException {
088        return getRealConnector().doHealthCheck(map);
089    }
090
091    @Override
092    public Application getApplication() {
093        return getRealConnector().getApplication();
094    }
095
096    @Override
097    public ConnectorServices getConnectorServices() {
098        return getRealConnector().getConnectorServices();
099    }
100
101    /** @deprecated */
102    @Override
103    @Deprecated
104    public String getConnectorType() {
105        return getRealConnector().getConnectorType();
106    }
107
108    @Override
109    public List<AttributeDefinition> getDefaultAttributes() {
110        Connector realConnector = getRealConnector();
111        if (realConnector instanceof AbstractConnector) {
112            return ((AbstractConnector)realConnector).getDefaultAttributes();
113        }
114        return super.getDefaultAttributes();
115    }
116
117    @Override
118    public List<Schema> getDefaultSchemas() {
119        Connector realConnector = getRealConnector();
120        if (realConnector instanceof AbstractConnector) {
121            return ((AbstractConnector)realConnector).getDefaultSchemas();
122        }
123        return super.getDefaultSchemas();
124    }
125
126    @Override
127    public Map<String, Object> getDependencyData() throws ConnectorException {
128        return getRealConnector().getDependencyData();
129    }
130
131    @Override
132    public String getInstance() {
133        return getRealConnector().getInstance();
134    }
135
136    @Override
137    public List<Partition> getIteratorPartitions(String s, int i, Filter filter, Map<String, Object> map) throws ConnectorException {
138        return getRealConnector().getIteratorPartitions(s, i, filter, map);
139    }
140
141    @Override
142    public ResourceObject getObject(String s, String s1, Map<String, Object> map) throws ConnectorException {
143        return getRealConnector().getObject(s, s1, map);
144    }
145
146    @Override
147    public Application getProxiedApplication(String s, Map<String, Object> map) throws ConnectorException {
148        return getRealConnector().getProxiedApplication(s, map);
149    }
150
151    /**
152     * Retrieves the actual Connector to delegate to, optionally loading from
153     * a plugin if needed. If no plugin is specified, this will use the ConnectorFactory
154     * to get the real connector class using the built-in functionality used by such
155     * things as logical connectors.
156     *
157     * The new connector instance will be cached and returned on subsequent calls.
158     *
159     * @return The connector class
160     * @throws IllegalArgumentException if any failures to get the connector occur
161     */
162    protected Connector getRealConnector() throws IllegalArgumentException {
163        if (_realConnector != null) {
164            return _realConnector;
165        }
166        synchronized(this) {
167            if (_realConnector != null) {
168                return _realConnector;
169            }
170            try {
171                String plugin = super.getStringAttribute("pluginName");
172                String connectorClass = super.getObligatoryStringAttribute("realConnectorClass");
173
174                Connector connector;
175                if (Util.isNotNullOrEmpty(plugin)) {
176                    if (log.isDebugEnabled()) {
177                        log.debug("Constructing delegated connector from plugin = " + plugin + ", className = " + connectorClass);
178                    }
179                    connector = PluginsUtil.instantiate(plugin, connectorClass, new Object[] { getApplication() }, new Class[] { Application.class});
180                    if (connector == null) {
181                        throw new InvalidConfigurationException("Could not find connector class " + connectorClass + " in plugin " + plugin);
182                    }
183                } else {
184                    if (log.isDebugEnabled()) {
185                        log.debug("Constructing delegated connector with className = " + connectorClass);
186                    }
187                    connector = super.getRealConnector(connectorClass);
188                }
189
190                connector.setInstance(super.getInstance());
191                connector.setConnectorServices(super.getConnectorServices());
192                connector.setStatisticsCollector(super.getStatisticsCollector());
193
194                _realConnector = connector;
195                return connector;
196            } catch (ConnectorException e) {
197                throw new IllegalArgumentException(e);
198            }
199        }
200    }
201
202    @Override
203    public List<Application.Feature> getSupportedFeatures() {
204        Connector realConnector = getRealConnector();
205        if (realConnector instanceof AbstractConnector) {
206            return ((AbstractConnector)realConnector).getSupportedFeatures();
207        }
208        return super.getSupportedFeatures();
209    }
210
211    @Override
212    public String getSystemIdentity() {
213        return getRealConnector().getSystemIdentity();
214    }
215
216    @Override
217    public Application getTargetApplication() {
218        return getRealConnector().getTargetApplication();
219    }
220
221    @Override
222    public String getTargetInstance() {
223        return getRealConnector().getTargetInstance();
224    }
225
226    @Override
227    public CloseableIterator<ResourceObject> iterateObjects(String s, Filter filter, Map<String, Object> map) throws ConnectorException {
228        return getRealConnector().iterateObjects(s, filter, map);
229    }
230
231    @Override
232    public CloseableIterator<ResourceObject> iterateObjects(Partition partition) throws ConnectorException {
233        return getRealConnector().iterateObjects(partition);
234    }
235
236    @Override
237    public ProvisioningResult provision(ProvisioningPlan provisioningPlan) throws ConnectorException, GeneralException {
238        return getRealConnector().provision(provisioningPlan);
239    }
240
241    @Override
242    public void saveConnectorState() {
243        Connector rc = getRealConnector();
244        if (rc instanceof AbstractConnector) {
245            ((AbstractConnector) rc).saveConnectorState();
246        }
247    }
248
249    @Override
250    public void saveConnectorState(Map<String, Object> stateMap) {
251        Connector rc = getRealConnector();
252        if (rc instanceof AbstractConnector) {
253            ((AbstractConnector) rc).saveConnectorState(stateMap);
254        }
255    }
256
257    @Override
258    public void setApplication(Application application) {
259        getRealConnector().setApplication(application);
260    }
261
262    @Override
263    public void setConnectorServices(ConnectorServices connectorServices) {
264        getRealConnector().setConnectorServices(connectorServices);
265    }
266
267    @Override
268    public void setInstance(String s) {
269        getRealConnector().setInstance(s);
270    }
271
272    @Override
273    public void setSystemIdentity(String s) {
274        getRealConnector().setSystemIdentity(s);
275    }
276
277    @Override
278    public void setTargetApplication(Application application) {
279        getRealConnector().setTargetApplication(application);
280    }
281
282    @Override
283    public void setTargetInstance(String s) {
284        getRealConnector().setTargetInstance(s);
285    }
286
287    @Override
288    public boolean shouldRetry(Exception ex, String error, ProvisioningResult result) {
289        boolean shouldRetry = super.shouldRetry(ex, error, result);
290        if (shouldRetry) {
291            return true;
292        }
293        Connector realConnector = getRealConnector();
294        if (realConnector instanceof AbstractConnector) {
295            shouldRetry = ((AbstractConnector)realConnector).shouldRetry(ex, error, result);
296        }
297        if (shouldRetry) {
298            return true;
299        }
300        String retryDetectionRule = getApplication().getStringAttributeValue("retryDetectionRule");
301        if (Util.isNotNullOrEmpty(retryDetectionRule)) {
302            if (log.isDebugEnabled()) {
303                log.debug("A retry detection rule " + retryDetectionRule + " is defined");
304            }
305
306            try {
307                Map<String, Object> inputs = new HashMap<>();
308                inputs.put("application", getApplication());
309                inputs.put("connector", this);
310                inputs.put("error", error);
311                inputs.put("result", result);
312                inputs.put("exception", ex);
313                inputs.put("connectorServices", getConnectorServices());
314                inputs.put("log", log);
315                Object output = getConnectorServices().runRule((Object) retryDetectionRule, inputs);
316                if (output instanceof Boolean) {
317                    if (log.isDebugEnabled()) {
318                        log.debug("The retry detection rule " + retryDetectionRule + " says we should retry this operation");
319                    }
320                    return (Boolean)output;
321                }
322            } catch(Exception e) {
323                log.error("Caught an error trying to determine if we should retry an operation with former outcome " + ex, e);
324            }
325        }
326        return false;
327    }
328
329    @Override
330    public boolean supportsPartitionedDeltaAggregation() {
331        return getRealConnector().supportsPartitionedDeltaAggregation();
332    }
333
334    @Override
335    public void testConfiguration() throws ConnectorException {
336        getRealConnector().testConfiguration();
337    }
338
339    @Override
340    public void updateConnectorState(Map<String, Object> map) {
341        Connector rc = getRealConnector();
342        if (rc instanceof ConnectorStateChangeListener) {
343            ((ConnectorStateChangeListener) rc).updateConnectorState(map);
344        }
345    }
346}