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}