/* * codjo.net * * Common Apache License 2.0 */ package net.codjo.operation.treatment; import net.codjo.expression.ExpressionException; import net.codjo.expression.ExpressionManager; import net.codjo.operation.AnomalyReport; import net.codjo.operation.OperationInterruptedException; import net.codjo.utils.QueryHelper; import net.codjo.utils.SqlTypeConverter; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import org.apache.log4j.Logger; /** * G�re le traitement d'un lot. * * <p> * Un lot possede deux mode d'ecriture Insert ou Update. Dans le mode update, la clause * where de la requete est: <code>updateDestKey=x</code> , ou <code>updateDestKey</code> * est le nom physique d'une colonne dans la table destination, et <code>x</code> est la * valeur courante de la colonne <code>updateSourceKey</code> . Dans ce mode une * expression "updateDestKey = updateSourceKey" est insere dans * l'<code>ExpressionManager</code> . * </p> * * @author $Author: marcona $ * @version $Revision: 1.9 $ */ public class TreatmentUnit { public static final Logger USER = Logger.getLogger("journal"); // Log private static final Logger APP = Logger.getLogger(TreatmentUnit.class); private BreakDetector breakDetector; private Object criterionZeroVal; private String destTableName; private ExpressionManager expressionManager; private int id; // private int batchCount = 0; // Criteres d'insertion private String insertCriterion = null; private int nbError; // Anomaly Report private AnomalyReport report; private TreatmentUnitSelection unitSelection; private String updateCriteria; private String updateDestKey; // Mode update private boolean updateMode = false; private String updateSourceKey; /** * Constructor for the TreatmentUnit object * * @param id Identifiant du lot * @param expressionManager * @param breakDetector * @param unitSelection * @param destTableName * @param insertCriterion Critere d'insertion * @param writeMode Mode d'ecriture * @param updateSourceKey La clef d'update dans la table source * @param updateDestKey La clef d'update dans la table destination * @param updateCriteria Le crit�re utilis� pour compl�ter la clause where de la * requ�te lorsqu'on est en mode update */ public TreatmentUnit(int id, ExpressionManager expressionManager, BreakDetector breakDetector, TreatmentUnitSelection unitSelection, String destTableName, String insertCriterion, String writeMode, String updateSourceKey, String updateDestKey, String updateCriteria) { this.expressionManager = expressionManager; this.breakDetector = breakDetector; this.unitSelection = unitSelection; this.destTableName = destTableName; this.id = id; this.updateCriteria = updateCriteria; setWriteMode(writeMode, updateSourceKey, updateDestKey); expressionManager.initExpressions(); setInsertCriterion(insertCriterion); } /** * Gets the Id attribute of the TreatmentUnit object * * @return The Id value */ public int getId() { return id; } /** * Lance le traitement du lot. * * @param con La connection utilis�e par le traitement. * @param operation L'operation (interface permettant d'adapter le code en fonction * de l'application). * * @exception SQLException Pb base. * @exception OperationInterruptedException Description of Exception */ public void proceed(Connection con, OperationData operation) throws SQLException, OperationInterruptedException { report = operation.getAnomalyReport(); report.clearAnomaly(); breakDetector.clear(); // Log if (APP.isDebugEnabled()) { APP.debug("D�but Traitement du Lot N� "+getId() +". "); } Statement selectStmt; if (report.needsSourceUpdatable()) { selectStmt = con.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE); } else { selectStmt = con.createStatement(); } ResultSet rs = unitSelection.doSelectUnit(con, selectStmt, operation); if (rs.next() == false) { return; } List destColumns = expressionManager.getDestFieldList(); List destSqlType = buildSqlTypeList(destColumns); PreparedStatement writeStmt = buildWriteStatement(con, destColumns, operation); try { nbError = 0; do { if (Thread.interrupted()) { throw new OperationInterruptedException("Interruption utilisateur"); } operation.getLoadedBehavior().incrementCurrentOfTask(); if (breakDetector.isBreakPoint(rs)) { if (report.isWriteAllowed()) { doWrite(writeStmt, destColumns, destSqlType); } report.clearAnomaly(); } if (report.hasAnomaly() == false) { doCompute(rs); report.updateSource(rs); } } while (rs.next()); if (report.isWriteAllowed()) { doWrite(writeStmt, destColumns, destSqlType); } // writeStmt.executeBatch(); // writeStmt.clearBatch(); } finally { // Log if (APP.isDebugEnabled()) { APP.debug("Fin Traitement du Lot. "); } writeStmt.close(); selectStmt.close(); rs.close(); unitSelection.updateSourceTableAnomalies(con); } } int getNbError() { return nbError; } /** * Rempli la liste des types SQL des champs de destination. * * @param destColumns Liste des noms de colonne de destination. * * @return Liste des types SQL. */ private List buildSqlTypeList(List destColumns) { List destSqlType = new ArrayList(destColumns.size()); for (int i = 0; i < destColumns.size(); i++) { Object columnName = destColumns.get(i); Object sqlType = expressionManager.getDestColumn().get(columnName); destSqlType.add(i, sqlType); } return destSqlType; } /** * Creation de la requete d'ecriture dans la table destination. Retourne une requete * insert ou update (suivant le mode d'ecriture) * * On insert les champs anomalies � la fin de la requ�te en mode insert et non en mode update. * * @param con La connection portant le PreparedStatement * @param destColumns Liste des colonnes destination * @param ope L'operation (interface permettant d'adapter le code en fonction de * l'application). * * @return le <code>PreparedStatement</code> * * @exception SQLException Erreur BD */ private PreparedStatement buildWriteStatement(Connection con, List destColumns, OperationData ope) throws SQLException { if (updateMode) { // Log if (APP.isDebugEnabled()) { APP.debug("\tMode Update"); } // UPDATE Statement // Dans ce mode : destColumns contient en derniere position, la // colonne utilise pour la clause where. List columns = new ArrayList(destColumns.subList(0, destColumns.size() - 1)); List whereList = destColumns.subList(destColumns.size() - 1, destColumns.size()); // Mise � jour des anomalies vers la table Destination ? Si oui, on ajoute les champs Anomalies if (report.needsDestinationUpdatable()) { columns.addAll(Arrays.asList(report.getColumnsName())); } if (updateCriteria != null) { int idx = updateCriteria.indexOf("$CURRENT_PERIOD$"); if (idx >= 0) { StringBuffer criteria = new StringBuffer(updateCriteria); criteria.replace(idx, idx + 16, "'" + ope.getPeriod() + "'"); updateCriteria = criteria.toString(); } int idxprev = updateCriteria.indexOf("$PREVIOUS_PERIOD$"); if (idxprev >= 0) { StringBuffer criteria = new StringBuffer(updateCriteria); criteria.replace(idxprev, idxprev + 17, "'" + ope.getPreviousPeriod() + "'"); updateCriteria = criteria.toString(); } } return QueryHelper.buildUpdateStatementWithWhereClause(destTableName, columns, whereList, updateCriteria, con); } else { // INSERT Statement // Log if (APP.isDebugEnabled()) { APP.debug("\tMode Insert"); } List columns = new ArrayList(destColumns); // Mise � jour des anomalies vers la table Destination ? Si oui, on ajoute les champs Anomalies if (report.needsDestinationUpdatable()) { columns.addAll(Arrays.asList(report.getColumnsName())); } return QueryHelper.buildInsertStatement(destTableName, columns, con); } } /** * Indique si on peut inserer. On insere que si la valeur du critere d'insertion est * differente de zero. * * @return 'true' si le critere d'insertion est valide (non null) */ private boolean canInsert() { if (insertCriterion == null) { return true; } Object value = expressionManager.getComputedValue(insertCriterion); if (value == null || ((Comparable)criterionZeroVal).compareTo(value) == 0) { return false; } else { return true; } } /** * Evaluation des expressions d'une ligne. Cette m�thode met � jour les champs * d'anomalies. * * @param rs ResultSet pointant sur la ligne � �valuer. * * @exception SQLException Description of Exception */ private void doCompute(ResultSet rs) throws SQLException { try { fillSourceField(rs); expressionManager.compute(); } catch (ExpressionException ex) { for (int i = 0; i < ex.getNbError(); i++) { report.addAnomaly(ex.getMessage(i)); } nbError++; } } private void doInsertTrace(List destColumns, SQLException ex) { StringBuffer errorMessage = new StringBuffer("Erreur lors de l'insertion :["); for (int i = 0; i < destColumns.size(); i++) { String columnName = (String)destColumns.get(i); Object columnValue = expressionManager.getComputedValue(columnName); errorMessage.append(columnName).append("=").append(columnValue); if (i + 1 < destColumns.size()) { errorMessage.append(","); } } errorMessage.append("]"); USER.error(errorMessage.toString(), ex); System.err.println(errorMessage.toString()); } /** * Lance l'insertion dans la base. Le statement est rempli dans cette m�thode. * * @param writeStmt Le Statement d'insert. * @param destColumns * @param destSqlType * * @exception SQLException Pb base. * */ private void doWrite(PreparedStatement writeStmt, List destColumns, List destSqlType) throws SQLException { if (canInsert() == false) { return; } // S�paration des champs � updater et ceux de la clause where // pour l'insertion de ceux du Rapport d'anomalie (ANOMALY, ANOMALY_LOG) List columns = new ArrayList(destColumns.subList(0, destColumns.size() - 1)); // List whereList = // destColumns.subList(destColumns.size() - 1, destColumns.size()); // Ajout dans le statement le type de l'objet // sauf pour la derni�re colonne car c'est le champ de la clause where int splitPosition= destColumns.size(); // Si Update Alors on Split la derni�re colonne pour ins�rer les colonnes anomaly if (report.needsDestinationUpdatable()){ if(updateMode) splitPosition=splitPosition-1; } for (int i = 0; i < splitPosition; i++) { String columnName = (String)destColumns.get(i); Integer sqlType = (Integer)destSqlType.get(i); writeStmt.setObject(i + 1, expressionManager.getComputedValue(columnName), sqlType.intValue()); } // Si Update alors Ajout du dernier champ spliter pr�c�demment if (report.needsDestinationUpdatable()) { // Ajout des champs et donn�es anomalies report.updateDestination(writeStmt, splitPosition + 1); if(updateMode){ // Ajout du dernier champ cas Split (clause where) String columnName = (String)destColumns.get(destColumns.size() - 1); Integer sqlType = (Integer)destSqlType.get(destColumns.size() - 1); writeStmt.setObject(splitPosition+3, expressionManager.getComputedValue(columnName), sqlType.intValue()); } } try { writeStmt.executeUpdate(); } catch (SQLException ex) { doInsertTrace(destColumns, ex); throw ex; } finally { writeStmt.clearParameters(); expressionManager.clear(); } } /** * Rempli les variables des champs source dans l'expressionManager. * * @param rs ResultSet pointant sur la ligne � �valuer. * * @exception SQLException Pb base. */ private void fillSourceField(ResultSet rs) throws SQLException { Iterator iter = expressionManager.getSourceColumn().keySet().iterator(); while (iter.hasNext()) { String columnName = (String)iter.next(); Object value = rs.getObject(columnName); expressionManager.setFieldSourceValue(columnName, value); } } /** * Positionne le critere d'insertion. * * @param insertCriterion * * @throws IllegalArgumentException TODO */ private void setInsertCriterion(String insertCriterion) { if (insertCriterion != null && "".equals(insertCriterion.trim()) == false) { this.insertCriterion = insertCriterion; int sqlType = expressionManager.getDestFieldSQLType(insertCriterion); if (expressionManager.isDestFieldNumeric(insertCriterion) == true) { criterionZeroVal = SqlTypeConverter.getDefaultSqlValue(sqlType); } else if (SqlTypeConverter.isString(new Integer(sqlType))) { criterionZeroVal = "#NR"; } else { throw new IllegalArgumentException("Le critere d'insertion n'est" + " pas un numerique ou une chaine de caract�res : " + insertCriterion); } } } /** * Positionne le mode d'ecriture du lot. * * @param writeMode Le mode d'ecriture ("INSERT" ou "UPDATE") * @param updateSourceKey La clef d'update dans la table source * @param updateDestKey La clef d'update dans la table destination * * @throws IllegalArgumentException TODO */ private void setWriteMode(String writeMode, String updateSourceKey, String updateDestKey) { if ("INSERT".equals(writeMode)) { this.updateMode = false; this.updateDestKey = null; this.updateSourceKey = null; } else if ("UPDATE".equals(writeMode)) { this.updateMode = true; this.updateDestKey = updateDestKey; this.updateSourceKey = updateSourceKey; expressionManager.add(updateDestKey, "SRC_" + updateSourceKey); } else { throw new IllegalArgumentException("Mode d'ecriture inconnue : " + writeMode); } } }