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}