001: /**
002: * Objective Database Abstraction Layer (ODAL)
003: * Copyright (c) 2004, The ODAL Development Group
004: * All rights reserved.
005: * For definition of the ODAL Development Group please refer to LICENCE.txt file
006: *
007: * Distributable under LGPL license.
008: * See terms of license at gnu.org.
009: */package com.completex.objective.components.persistency.core.impl;
010:
011: import com.completex.objective.components.persistency.ColumnType;
012: import com.completex.objective.components.persistency.InsertPersistency;
013: import com.completex.objective.components.persistency.InsertPersistency2;
014: import com.completex.objective.components.persistency.LifeCycleController;
015: import com.completex.objective.components.persistency.MetaColumn;
016: import com.completex.objective.components.persistency.OdalPersistencyException;
017: import com.completex.objective.components.persistency.PersistentObject;
018: import com.completex.objective.components.persistency.Record;
019: import com.completex.objective.components.persistency.transact.Transaction;
020: import com.completex.objective.components.persistency.transact.TransactionListener;
021: import com.completex.objective.components.persistency.transact.TransactionManager;
022: import com.completex.objective.components.persistency.transact.impl.TransactionContainer;
023: import com.completex.objective.util.StringUtil;
024: import com.completex.objective.util.TypeUtil;
025:
026: import java.io.File;
027: import java.io.FileNotFoundException;
028: import java.io.FileOutputStream;
029: import java.io.IOException;
030: import java.io.PrintWriter;
031: import java.io.Writer;
032: import java.sql.CallableStatement;
033: import java.sql.Connection;
034: import java.sql.PreparedStatement;
035: import java.sql.SQLException;
036: import java.sql.Statement;
037: import java.text.DateFormat;
038: import java.util.Date;
039: import java.util.Iterator;
040: import java.util.LinkedHashMap;
041: import java.util.List;
042: import java.util.Map;
043:
044: /**
045: * @author Gennady Krizhevsky
046: */
047: public abstract class SqlLoaderPersistencyImpl implements
048: TransactionManager, InsertPersistency, InsertPersistency2 {
049:
050: //
051: // Consts:
052: //
053: public static final String DEFAULT_FIELD_DELIMITER = "|";
054: public static final String DEFAULT_ORACLE_DATE_FORMAT = "YYYY-MM-DD";
055: public static final String DEFAULT_CTL_FILE_EXT = "ctl";
056: public static final String DEFAULT_DATA_FILE_EXT = "dat";
057: public static final String DEFAULT_LOG_FILE_EXT = "nlog";
058:
059: //
060: // Vars:
061: //
062: protected String oracleDateFormat = DEFAULT_ORACLE_DATE_FORMAT;
063:
064: protected DateFormat javaDateFormat = TypeUtil.yyyy_MM_dd();
065:
066: protected String fieldDelimiter = DEFAULT_FIELD_DELIMITER;
067:
068: private DateFormat fileDateFormat = TypeUtil.yyyyMMdd();
069:
070: private int maxBatchDigits = 6;
071:
072: private String ctlFileExt = DEFAULT_CTL_FILE_EXT;
073:
074: private String dataFileExt = DEFAULT_DATA_FILE_EXT;
075:
076: private String logFileExt = DEFAULT_LOG_FILE_EXT;
077:
078: private TransactionContainer transactionContainer = new TransactionContainer();
079:
080: protected SqlLoaderPersistencyImpl(String fieldDelimiter,
081: DateFormat fileDateFormat, int maxBatchDigits,
082: String ctlFileExt, String dataFileExt, String logFileExt) {
083: this .fieldDelimiter = fieldDelimiter;
084: this .fileDateFormat = fileDateFormat;
085: this .maxBatchDigits = maxBatchDigits;
086: this .ctlFileExt = ctlFileExt;
087: this .dataFileExt = dataFileExt;
088: this .logFileExt = logFileExt;
089: }
090:
091: protected SqlLoaderPersistencyImpl() {
092: }
093:
094: public SqlLoaderPersistencyImpl(String fieldDelimiter) {
095: this .fieldDelimiter = fieldDelimiter;
096: }
097:
098: protected PrintWriter getDataWriter(Transaction transaction,
099: PersistentObject persistentObject)
100: throws OdalPersistencyException {
101: SqlLoaderTransaction sqlLoaderTransaction = (SqlLoaderTransaction) transaction;
102: String tableName = persistentObject.record().getTableName();
103: PrintWriter dataWriter = sqlLoaderTransaction
104: .getPrintWriter(tableName);
105:
106: if (dataWriter == null) {
107: String fileName = generateFileName(sqlLoaderTransaction,
108: persistentObject);
109: String ctlFileName = fileName + "." + ctlFileExt;
110: String dataFileName = fileName + "." + dataFileExt;
111: String ctlFilePath = generateFilePath(sqlLoaderTransaction,
112: ctlFileName);
113: String dataFilePath = generateFilePath(
114: sqlLoaderTransaction, dataFileName);
115: PrintWriter ctlWriter = null;
116: try {
117: ctlWriter = new PrintWriter(new FileOutputStream(
118: ctlFilePath));
119: createControlFile(persistentObject.record(),
120: dataFileName, fieldDelimiter, ctlWriter,
121: oracleDateFormat);
122: dataWriter = new PrintWriter(new FileOutputStream(
123: dataFilePath));
124: } catch (FileNotFoundException e) {
125: throw new OdalPersistencyException(
126: "Failed to create file. FileNotFoundException: "
127: + e.getMessage());
128: } finally {
129:
130: if (ctlWriter != null) {
131: try {
132: ctlWriter.flush();
133: } catch (Exception e) {
134: }
135: try {
136: ctlWriter.close();
137: } catch (Exception e) {
138: }
139: }
140: }
141: sqlLoaderTransaction.putPrintWriter(tableName, dataWriter);
142: }
143: return dataWriter;
144: }
145:
146: protected PrintWriter getLogWriter(Transaction transaction,
147: PersistentObject persistentObject) {
148: SqlLoaderTransaction sqlLoaderTransaction = (SqlLoaderTransaction) transaction;
149: String tableName = persistentObject.record().getTableName();
150: String logKey = "log" + tableName;
151: PrintWriter logWriter = sqlLoaderTransaction
152: .getPrintWriter(logKey);
153:
154: if (logWriter == null) {
155: String fileName = generateFileName(sqlLoaderTransaction,
156: persistentObject);
157: String logFileName = fileName + "." + logFileExt;
158: String logFilePath = generateFilePath(sqlLoaderTransaction,
159: logFileName);
160: try {
161: //
162: logWriter = new PrintWriter(new FileOutputStream(
163: logFilePath));
164: } catch (FileNotFoundException e) {
165: throw new RuntimeException("FileNotFoundException: "
166: + logFilePath + "; " + e.getMessage(), e);
167: }
168: sqlLoaderTransaction.putPrintWriter(logKey, logWriter);
169: }
170: return logWriter;
171: }
172:
173: protected String getDataFileName(Transaction transaction,
174: PersistentObject persistentObject) {
175: SqlLoaderTransaction sqlLoaderTransaction = (SqlLoaderTransaction) transaction;
176: return generateFileName(sqlLoaderTransaction, persistentObject);
177: }
178:
179: public int insert(Transaction transaction,
180: PersistentObject persistentObject)
181: throws OdalPersistencyException {
182: return writeToDataFile(transaction, persistentObject,
183: getDataWriter(transaction, persistentObject),
184: fieldDelimiter, javaDateFormat);
185: }
186:
187: public int insert(Transaction transaction,
188: PersistentObject persistentObject,
189: LifeCycleController controller)
190: throws OdalPersistencyException {
191: return writeToDataFile(transaction, persistentObject,
192: getDataWriter(transaction, persistentObject),
193: fieldDelimiter, javaDateFormat, controller);
194: }
195:
196: public int writeToDataFile(Transaction transaction,
197: PersistentObject persistentObject, PrintWriter writer,
198: String fieldDelimiter, DateFormat dateFormat) {
199: return writeToDataFile(transaction, persistentObject, writer,
200: fieldDelimiter, dateFormat,
201: LifeCycleController.NULL_LIFE_CYCLE_CONTROLLER);
202: }
203:
204: public static final String NL = "\n";
205:
206: public int writeToDataFile(Transaction transaction,
207: PersistentObject persistentObject, PrintWriter writer,
208: String fieldDelimiter, DateFormat dateFormat,
209: LifeCycleController controller) {
210:
211: // assert persistentObject != null;
212: Record record = persistentObject.record();
213:
214: // If record has keys only dirty && skipInsertForKeysOnly is set to true - skip
215: if (record.isSkipInsertForKeysOnly()
216: && !record.hasDirtyNonKeyFields()) {
217: return 0;
218: }
219: controller = controller == null ? LifeCycleController.NULL_LIFE_CYCLE_CONTROLLER
220: : controller;
221:
222: StringBuffer buffer = new StringBuffer();
223: int last = record.size() - 1;
224: for (int i = 0; i < record.size(); i++) {
225: ColumnType type = record.getColumn(i).getType();
226: Object value = record.getObject(i);
227: if (value != null) {
228: if (ColumnType.isString(type)) {
229: buffer.append(value);
230: } else if (ColumnType.DATE == type) {
231: // buffer.append("\"").append(TypeUtil.D2S((Date) value, dateFormat)).append("\"");
232: buffer.append(TypeUtil
233: .D2S((Date) value, dateFormat));
234: } else if (ColumnType.BOOLEAN == type) {
235: buffer.append(TypeUtil.B2S((Boolean) value,
236: TypeUtil.Y_N));
237: } else {
238: buffer.append(value);
239: }
240: }
241:
242: if (i < last) {
243: buffer.append(fieldDelimiter);
244: }
245: }
246:
247: try {
248: // Before:
249: controller.beforeInsert(persistentObject);
250:
251: writer.println(buffer);
252: writer.flush();
253: } catch (RuntimeException e) {
254: String message = "ERROR: " + e.getMessage();
255: StackTraceElement[] traceElements = e.getStackTrace();
256: PrintWriter logWriter = getLogWriter(transaction,
257: persistentObject);
258: if (traceElements != null) {
259: for (int i = 0; i < traceElements.length; i++) {
260: message += " " + traceElements[i].toString() + NL;
261: }
262: }
263:
264: if (logWriter != null) {
265: logWriter.write(message + "LINE: " + buffer + NL);
266: try {
267: if (controller.getParameters() != null) {
268: for (int i = 0; i < controller.getParameters().length; i++) {
269: if (controller.getParameters()[i] != null) {
270: printError(
271: controller.getParameters()[i],
272: logWriter);
273: }
274: }
275: }
276: } catch (IOException e1) {
277: e1.printStackTrace();
278: }
279: logWriter.flush();
280: }
281: throw e;
282: }
283:
284: return 1;
285: }
286:
287: public static void createControlFile(Record record,
288: String dataFileName, String fieldDelimiter,
289: PrintWriter writer, String oracleDateFormat) {
290: // assert record != null;
291: // assert dataFileName != null;
292: // assert fieldDelimiter != null;
293: // assert writer != null;
294:
295: // Header:
296: String[] strings = composeHeader(dataFileName, record,
297: fieldDelimiter);
298: for (int i = 0; i < strings.length; i++) {
299: writer.println(strings[i]);
300: }
301: writer.println(new StringBuffer().append("("));
302:
303: // Body:
304: for (int i = 0; i < record.size(); i++) {
305: StringBuffer buffer = new StringBuffer(record.getColumn(i)
306: .getColumnName()).append(" ");
307: type2loader(buffer, record.getColumn(i), oracleDateFormat);
308: if (i < record.size() - 1) {
309: buffer.append(",");
310: }
311: writer.println(buffer);
312: writer.flush();
313: }
314: writer.println(")");
315: writer.flush();
316: }
317:
318: private static String[] composeHeader(String dataFileName,
319: Record record, String fieldDelimiter) {
320: String[] strings = { "LOAD DATA",
321: "INFILE '" + dataFileName + "'",
322: "APPEND PRESERVE BLANKS",
323: "INTO TABLE " + record.getTableName(),
324: "FIELDS TERMINATED BY '" + fieldDelimiter + "'",
325: "TRAILING NULLCOLS", };
326: return strings;
327: }
328:
329: public static String createControlFileAsString(Record record,
330: String dataFileName, String fieldDelimiter,
331: String oracleDateFormat) {
332: // assert record != null;
333: // assert dataFileName != null;
334: // assert fieldDelimiter != null;
335:
336: StringBuffer buffer = new StringBuffer();
337:
338: // Header:
339: String[] strings = composeHeader(dataFileName, record,
340: fieldDelimiter);
341: for (int i = 0; i < strings.length; i++) {
342: buffer.append(strings[i]).append(NL);
343: }
344: buffer.append("(").append(NL);
345:
346: // Body:
347: for (int i = 0; i < record.size(); i++) {
348: buffer.append(record.getColumn(i).getColumnName()).append(
349: " ");
350: type2loader(buffer, record.getColumn(i), oracleDateFormat);
351: if (i < record.size() - 1) {
352: buffer.append(",");
353: }
354: buffer.append(NL);
355: }
356: buffer.append(")");
357: return buffer.toString();
358: }
359:
360: private static StringBuffer type2loader(StringBuffer buffer,
361: MetaColumn column, String oracleDateFormat) {
362: ColumnType type = column.getType();
363: if (ColumnType.DATE == type) {
364: return buffer.append("DATE \"").append(oracleDateFormat)
365: .append("\"");
366: } else {
367: return buffer;
368: }
369: // if (ColumnType.isString(type) || ColumnType.BOOLEAN == type) {
370: //// return buffer.append("CHAR NULLIF(").append(column.getColumnName()).append("=\" \")");
371: // return buffer.append("CHAR(").append(column.getColumnSize()).append(")");
372: // } else if (ColumnType.DATE == type) {
373: // return buffer.append("DATE \"").append(oracleDateFormat).append("\"");
374: // } else if (ColumnType.LONG == column.getType()) {
375: //// return buffer.append("INTEGER EXTERNAL NULLIF(").append(column.getColumnName()).append("=0)");
376: // return buffer.append("INTEGER(").append(column.getColumnSize()).append(")");
377: // } else if (ColumnType.DOUBLE == column.getType()) {
378: //// return buffer.append("DECIMAL EXTERNAL NULLIF(").append(column.getColumnName()).append("=0)");
379: // return buffer.append("DECIMAL(").append(column.getColumnSize()).append(",").append(column.getDecimalDigits()).append(")");
380: // } else {
381: // throw new RuntimeException("Unsupported type " + type);
382: // }
383: // return buffer;
384: }
385:
386: public Transaction getCurrentTransaction() {
387: return transactionContainer.peekTransaction();
388: }
389:
390: public boolean hasCurrentTransaction() {
391: return !transactionContainer.isEmpty();
392: }
393:
394: protected String generateFileName(
395: SqlLoaderTransaction sqlLoaderTransaction,
396: PersistentObject persistentObject) {
397: String tableName = persistentObject.record().getTableName();
398: String fileName = fileDateFormat.format(sqlLoaderTransaction
399: .getDate())
400: + "_"
401: + tableName
402: + "_"
403: + padNumber(sqlLoaderTransaction.getBatchNumber());
404: return fileName;
405: }
406:
407: private String generateFilePath(
408: SqlLoaderTransaction sqlLoaderTransaction, String fileName) {
409: return sqlLoaderTransaction.getOutDir() + "/" + fileName;
410: }
411:
412: private String padNumber(Number number) {
413: return StringUtil.lpadNumber(number, maxBatchDigits);
414: }
415:
416: protected void commit0(SqlLoaderTransaction sqlLoaderTransaction) {
417: try {
418: for (Iterator it = sqlLoaderTransaction.getWriters()
419: .keySet().iterator(); it.hasNext();) {
420: Object key = it.next();
421: PrintWriter writer = (PrintWriter) sqlLoaderTransaction
422: .getWriters().get(key);
423:
424: if (writer != null) {
425: try {
426: writer.flush();
427: } catch (Exception e) {
428: }
429: }
430: }
431: } finally {
432: releaseTransaction(sqlLoaderTransaction);
433: }
434: }
435:
436: public Transaction releaseTransaction(Transaction transaction) {
437: transactionContainer.validateTransactionForRelease(transaction);
438: return transactionContainer.popTransaction();
439: }
440:
441: protected void closeWriters(
442: SqlLoaderTransaction sqlLoaderTransaction) {
443: for (Iterator it = sqlLoaderTransaction.getWriters().keySet()
444: .iterator(); it.hasNext();) {
445: Object key = it.next();
446: PrintWriter writer = (PrintWriter) sqlLoaderTransaction
447: .getWriters().get(key);
448:
449: if (writer != null) {
450: try {
451: writer.close();
452:
453: } catch (Exception e) {
454: }
455: }
456: }
457: sqlLoaderTransaction.getWriters().clear();
458: }
459:
460: /**
461: * @param parameters 1st [0] - Transaction,
462: * 2nd [1] - batch number;
463: * 3rd [2] - outDir
464: * 4th [3] - optional Boolean -
465: * close transaction (close all the writers)
466: * @return Object
467: */
468: public Object reset(Object[] parameters) {
469: // assert parameters != null && parameters.length >= 2;
470: SqlLoaderTransaction transaction = (SqlLoaderTransaction) parameters[0];
471: Number oldBatchNumber = transaction.getBatchNumber();
472: Number batchNumber = (Number) parameters[1];
473: transaction.setBatchNumber(batchNumber);
474: if (parameters.length > 2) {
475: String outDir = (String) parameters[2];
476: transaction.setOutDir(outDir);
477: File outPath = new File(outDir);
478: outPath.mkdirs();
479: }
480:
481: if (parameters.length > 3) {
482: Boolean closeObj = (Boolean) parameters[3];
483: boolean close = closeObj != null && closeObj.booleanValue();
484: if (close) {
485: closeWriters(transaction);
486: }
487: }
488:
489: return oldBatchNumber;
490: }
491:
492: protected abstract void printError(Object parameter, Writer writer)
493: throws IOException;
494:
495: public Transaction begin() {
496: SqlLoaderTransaction transaction = new SqlLoaderTransaction();
497: transactionContainer.pushTransaction(transaction);
498: return transaction;
499: }
500:
501: public void commit(Transaction transaction) {
502: SqlLoaderTransaction sqlLoaderTransaction = (SqlLoaderTransaction) transaction;
503: commit0(sqlLoaderTransaction);
504: }
505:
506: public void rollback(Transaction transaction) {
507: // Do nothing?
508: commit(transaction);
509: }
510:
511: public Transaction beginUnchecked() {
512: return begin();
513: }
514:
515: public void commitUnchecked(Transaction transaction) {
516: commit(transaction);
517: }
518:
519: public void rollbackUnchecked(Transaction transaction) {
520: rollback(transaction);
521: }
522:
523: public void releaseUnchecked(Transaction transaction) {
524: release(transaction);
525: }
526:
527: public void rollbackSilently(Transaction transaction) {
528: if (transaction != null) {
529: try {
530: rollback(transaction);
531: } catch (Exception e) {
532: // Silently!
533: e.printStackTrace();
534: }
535: }
536: }
537:
538: public void release(Transaction transaction) {
539: releaseTransaction(transaction);
540: }
541:
542: public void shutdown() {
543: }
544:
545: public TransactionManager getTransactionManager() {
546: return this ;
547: }
548:
549: public int insert(PersistentObject persistentObject)
550: throws OdalPersistencyException {
551: return insert(getCurrentTransaction(), persistentObject);
552: }
553:
554: public int insert(PersistentObject persistentObject,
555: LifeCycleController controller)
556: throws OdalPersistencyException {
557: return insert(getCurrentTransaction(), persistentObject,
558: controller);
559: }
560:
561: static public class SqlLoaderTransaction implements Transaction {
562:
563: private Date date = new Date();
564: private String outDir;
565: private Number batchNumber;
566: Map writers = new LinkedHashMap();
567:
568: public void rollbackSilently() {
569:
570: }
571:
572: public String connectionToString() {
573: return toString();
574: }
575:
576: public Statement createStatement() throws SQLException {
577: throw new UnsupportedOperationException();
578: }
579:
580: public PreparedStatement prepareStatement(String sql) {
581: throw new UnsupportedOperationException();
582: }
583:
584: public CallableStatement prepareCall(String sql)
585: throws SQLException {
586: throw new UnsupportedOperationException();
587: }
588:
589: public List flush() throws SQLException {
590: return null;
591: }
592:
593: public void releaseStatement(PreparedStatement statement) {
594: throw new UnsupportedOperationException();
595: }
596:
597: public Connection getConnection() {
598: throw new UnsupportedOperationException();
599: }
600:
601: public Date getDate() {
602: return date;
603: }
604:
605: public void setDate(Date date) {
606: this .date = date;
607: }
608:
609: public Number getBatchNumber() {
610: return batchNumber;
611: }
612:
613: public void setBatchNumber(Number batchNumber) {
614: this .batchNumber = batchNumber;
615: }
616:
617: public Map getWriters() {
618: return writers;
619: }
620:
621: public void putPrintWriter(String key, PrintWriter printWriter) {
622: writers.put(key, printWriter);
623: }
624:
625: public PrintWriter getPrintWriter(String key) {
626: return (PrintWriter) writers.get(key);
627: }
628:
629: public void commit() {
630: for (Iterator it = this .getWriters().keySet().iterator(); it
631: .hasNext();) {
632: Object key = it.next();
633: PrintWriter writer = (PrintWriter) this .getWriters()
634: .get(key);
635:
636: if (writer != null) {
637: try {
638: writer.flush();
639: } catch (Exception e) {
640: }
641: }
642: }
643: }
644:
645: public void rollback() {
646: }
647:
648: public void commitUnchecked() {
649: commit();
650: }
651:
652: public void rollbackUnchecked() {
653: rollback();
654: }
655:
656: public String getOutDir() {
657: return outDir;
658: }
659:
660: public void setOutDir(String outDir) {
661: this .outDir = outDir;
662: }
663:
664: public void addListerner(Object key,
665: TransactionListener listener) {
666: }
667:
668: public TransactionListener getListener(Object key) {
669: return null;
670: }
671:
672: public boolean containsListener(Object key) {
673: return false;
674: }
675:
676: public void removeListerner(Object key) {
677: }
678:
679: public int listenersSize() {
680: return 0;
681: }
682:
683: public void clearListerners() {
684: }
685: }
686: }
|