001package com.identityworksllc.iiq.common.task.export; 002 003import com.identityworksllc.iiq.common.Functions; 004import com.identityworksllc.iiq.common.query.NamedParameterStatement; 005import com.identityworksllc.iiq.common.threads.SailPointWorker; 006import org.apache.commons.logging.Log; 007import sailpoint.api.SailPointContext; 008import sailpoint.object.Configuration; 009import sailpoint.object.TaskResult; 010import sailpoint.request.RequestPermanentException; 011import sailpoint.task.TaskMonitor; 012import sailpoint.tools.GeneralException; 013import sailpoint.tools.JdbcUtil; 014import sailpoint.tools.Message; 015import sailpoint.tools.Util; 016 017import java.io.Serializable; 018import java.sql.Connection; 019import java.sql.SQLException; 020import java.sql.Types; 021import java.util.Date; 022import java.util.Objects; 023 024/** 025 * An abstract superclass for all export partitions. It will open a connection to the target 026 * database and then invoke export() on the subclass. 027 */ 028public abstract class ExportPartition extends SailPointWorker implements Serializable { 029 030 /** 031 * Adds the common date fields (which must have the given names) to the given prepared statement. 032 * The modified and last refresh dates can be null. 033 * 034 * @param statement The named parameter statement 035 * @param exportDate The export date 036 * @param created The creation date 037 * @param modified The modified date (which can be null) 038 * @param lastRefresh The last refresh date (which can be null) 039 * @throws SQLException if any failures occur (unlikely) 040 */ 041 protected static void addCommonDateFields(NamedParameterStatement statement, Date exportDate, Date created, Date modified, Date lastRefresh) throws SQLException { 042 statement.setDate("created", created); 043 if (modified != null) { 044 statement.setDate("modified", modified); 045 } else { 046 statement.setNull("modified", Types.DATE); 047 } 048 if (lastRefresh != null) { 049 statement.setDate("lastRefresh", lastRefresh); 050 } else { 051 statement.setNull("lastRefresh", Types.DATE); 052 } 053 statement.setDate("now", exportDate); 054 } 055 056 /** 057 * Opens the connection to the target database using the provided connection info 058 * @param context The sailpoint context, used to decrypt the password 059 * @param connectionInfo The provided connection info, extracted from the export task def 060 * @return The open connection 061 * @throws GeneralException if any failures occur 062 */ 063 public static Connection openConnection(SailPointContext context, ExportConnectionInfo connectionInfo) throws GeneralException { 064 String decryptedPassword = context.decrypt(connectionInfo.getEncryptedPassword()); 065 return JdbcUtil.getConnection(connectionInfo.getDriver(), null, connectionInfo.getUrl(), connectionInfo.getUsername(), decryptedPassword, connectionInfo.getOptions()); 066 } 067 068 /** 069 * The configuration loaded in {@link #execute(SailPointContext, Log)} 070 */ 071 protected transient Configuration configuration; 072 /** 073 * The name of the configuration object, set by the task 074 */ 075 private String configurationName; 076 /** 077 * The connection info 078 */ 079 private ExportConnectionInfo connectionInfo; 080 /** 081 * The cutoff date, milliseconds. We should not export records older than this date. 082 */ 083 protected long cutoffDate; 084 /** 085 * The export timestamp, milliseconds. We should not export records newer than this date. 086 */ 087 protected long exportTimestamp; 088 /** 089 * The filter string 090 */ 091 protected String filterString; 092 093 /** 094 * The second filter string if any 095 */ 096 protected String filterString2; 097 098 /** 099 * The name of the partition 100 */ 101 private String name; 102 103 /** 104 * The worker entrypoint 105 * @param context The private context to use for this thread worker 106 * @param logger The log attached to this Worker 107 * @return Always null, nothing required here 108 * @throws Exception if anything goes wrong 109 */ 110 @Override 111 public final Object execute(SailPointContext context, Log logger) throws Exception { 112 if (Util.isNullOrEmpty(configurationName)) { 113 throw new GeneralException("Unable to execute export worker: configurationName not set"); 114 } 115 this.configuration = context.getObjectByName(Configuration.class, configurationName); 116 if (this.configuration == null) { 117 throw new GeneralException("Unable to execute export worker: Configuration named '" + configurationName + "' does not exist"); 118 } 119 120 if (Util.isNotNullOrEmpty(filterString)) { 121 TaskResult partitionResult = monitor.lockPartitionResult(); 122 try { 123 partitionResult.setAttribute("filter", filterString + (filterString2 != null ? (" && " + filterString2) : "")); 124 } finally { 125 monitor.commitPartitionResult(); 126 } 127 } 128 129 try (Connection connection = openConnection(context, connectionInfo)) { 130 boolean previousAutoCommit = connection.getAutoCommit(); 131 connection.setAutoCommit(false); 132 try { 133 export(context, connection, logger); 134 connection.commit(); 135 } finally { 136 connection.setAutoCommit(previousAutoCommit); 137 } 138 } catch(Exception e) { 139 TaskResult partitionResult = monitor.lockPartitionResult(); 140 try { 141 partitionResult.addException(e); 142 } finally { 143 monitor.commitPartitionResult(); 144 } 145 throw new RequestPermanentException(e); 146 } 147 return null; 148 } 149 150 /** 151 * Exports the data required to the listed database 152 * 153 * @param context The context 154 * @param connection The connection to the target database 155 * @param logger The logger 156 * @throws GeneralException if any failures occur 157 */ 158 protected abstract void export(SailPointContext context, Connection connection, Log logger) throws GeneralException; 159 160 public Configuration getConfiguration() { 161 return configuration; 162 } 163 164 public String getConfigurationName() { 165 return configurationName; 166 } 167 168 public long getCutoffDate() { 169 return cutoffDate; 170 } 171 172 public long getExportTimestamp() { 173 return exportTimestamp; 174 } 175 176 public String getFilterString() { 177 return filterString; 178 } 179 180 public String getFilterString2() { 181 return filterString2; 182 } 183 184 public String getName() { 185 return name; 186 } 187 188 public void setConfigurationName(String configurationName) { 189 this.configurationName = configurationName; 190 } 191 192 public void setConnectionInfo(ExportConnectionInfo connectionInfo) { 193 this.connectionInfo = Objects.requireNonNull(connectionInfo, "connectionInfo"); 194 } 195 196 public void setCutoffDate(long cutoffDate) { 197 this.cutoffDate = cutoffDate; 198 } 199 200 public void setExportTimestamp(long exportTimestamp) { 201 this.exportTimestamp = exportTimestamp; 202 } 203 204 public void setFilterString(String filterString) { 205 this.filterString = filterString; 206 } 207 208 public void setFilterString2(String filterString2) { 209 this.filterString2 = filterString2; 210 } 211 212 public void setName(String name) { 213 this.name = name; 214 } 215}