001: package com.bm.testsuite.dataloader;
002:
003: import java.io.File;
004: import java.io.FileInputStream;
005: import java.io.FileNotFoundException;
006: import java.io.IOException;
007: import java.io.InputStream;
008: import java.net.URL;
009: import java.sql.Connection;
010: import java.sql.PreparedStatement;
011: import java.sql.SQLException;
012: import java.text.ParseException;
013: import java.util.ArrayList;
014: import java.util.Arrays;
015: import java.util.Date;
016: import java.util.List;
017:
018: import javax.persistence.EntityManager;
019: import javax.persistence.EntityTransaction;
020: import javax.persistence.Query;
021:
022: import com.bm.cfg.Ejb3UnitCfg;
023: import com.bm.introspectors.EmbeddedClassIntrospector;
024: import com.bm.introspectors.EntityBeanIntrospector;
025: import com.bm.introspectors.PersistentPropertyInfo;
026: import com.bm.introspectors.Property;
027: import com.bm.utils.BasicDataSource;
028: import com.bm.utils.Ejb3Utils;
029: import com.bm.utils.SQLUtils;
030: import com.bm.utils.csv.CSVParser;
031:
032: /**
033: * This class creates initial data from a comma separated file.
034: *
035: * @param <T>
036: * the type of the entity bean (mapping the table)
037: *
038: * @author Daniel Wiese
039: * @author Istvan Devai
040: * @author Peter Doornbosch
041: * @deprecated will be deleted soon
042: * @since 17.04.2006
043: */
044: public class CSVInitialDataNoRelationalSet<T> implements InitialDataSet {
045:
046: private static final org.apache.log4j.Logger log = org.apache.log4j.Logger
047: .getLogger(CSVInitialDataNoRelationalSet.class);
048:
049: private EntityBeanIntrospector<T> introspector;
050:
051: private String[] propertyMapping;
052:
053: private Property[] propertyInfo;
054:
055: private String insertSQLString;
056:
057: private final File file;
058:
059: private final boolean isCopressed;
060:
061: private List<DateFormats> userDefinedDateFormats = new ArrayList<DateFormats>();
062:
063: private final boolean useSchemaName;
064:
065: /**
066: * Constructor.
067: *
068: * @param entityBeanClass -
069: * the corresponding enetity bean class
070: * @param propertyMapping -
071: * a string array whith the meaning the first column of the cvs
072: * file belongs to the property with the name
073: * <code>propertyMapping[0]</code>
074: * @param isCompressed -
075: * true if compressed (zip)
076: * @param useSchemaName
077: * the schema name will be used for sql generation
078: * @param csvFileName -
079: * the name of the cvs file
080: */
081: public CSVInitialDataNoRelationalSet(Class<T> entityBeanClass,
082: String csvFileName, boolean isCompressed,
083: boolean useSchemaName, String... propertyMapping) {
084: this .useSchemaName = useSchemaName;
085: this .isCopressed = isCompressed;
086: initialize(entityBeanClass, propertyMapping);
087: final URL tmp = Thread.currentThread().getContextClassLoader()
088: .getResource(csvFileName);
089: if (tmp == null) {
090: throw new IllegalArgumentException(
091: "Canīt find the CVS file named (" + csvFileName
092: + ")");
093: }
094:
095: file = new File(Ejb3Utils.getDecodedFilename(tmp));
096:
097: }
098:
099: /**
100: * Constructor.
101: *
102: * @param entityBeanClass -
103: * the corresponding enetity bean class
104: * @param propertyMapping -
105: * a string array whith the meaning the first column of the cvs
106: * file belongs to the property with the name
107: * <code>propertyMapping[0]</code>
108: * @param isCompressed -
109: * true if compressed (zip)
110: * @param csvFileName -
111: * the name of the cvs file
112: */
113: public CSVInitialDataNoRelationalSet(Class<T> entityBeanClass,
114: String csvFileName, boolean isCompressed,
115: String... propertyMapping) {
116: this (entityBeanClass, csvFileName, isCompressed, false,
117: propertyMapping);
118: }
119:
120: /**
121: * Constructor.
122: *
123: * @param entityBeanClass -
124: * the corresponding enetity bean class
125: * @param propertyMapping -
126: * a string array whith the meaning the first column of the cvs
127: * file belongs to the property with the name
128: * <code>propertyMapping[0]</code>
129: * @param csvFileName -
130: * the name of the cvs file
131: */
132: public CSVInitialDataNoRelationalSet(Class<T> entityBeanClass,
133: String csvFileName, String... propertyMapping) {
134: this (entityBeanClass, csvFileName, false, false,
135: propertyMapping);
136: }
137:
138: /**
139: * Allows to specify a user specific date format pattern's. The are
140: * processed in the added order.
141: *
142: * @param dateFormat
143: * the date format pattern
144: * @return this instance for inlining.
145: */
146: public CSVInitialDataNoRelationalSet<T> addDateFormat(
147: DateFormats dateFormat) {
148: this .userDefinedDateFormats.add(dateFormat);
149: return this ;
150: }
151:
152: /**
153: * @author Daniel Wiese
154: * @since 29.06.2006
155: * @param entityBeanClass
156: * @param propertyMapping
157: */
158: private void initialize(Class<T> entityBeanClass,
159: String... propertyMapping) {
160: this .introspector = new EntityBeanIntrospector<T>(
161: entityBeanClass);
162: // init configuration if not yet done
163: if (!Ejb3UnitCfg.isInitialized()) {
164: List<Class<? extends Object>> usedBeans = new ArrayList<Class<? extends Object>>();
165: usedBeans.add(entityBeanClass);
166: Ejb3UnitCfg.addEntytiesToTest(usedBeans);
167: // call the factory to create the tables
168: Ejb3UnitCfg.getConfiguration().getEntityManagerFactory();
169: }
170: this .propertyMapping = propertyMapping;
171: this .propertyInfo = new Property[propertyMapping.length];
172: this .insertSQLString = this .buildInsertSQL();
173: }
174:
175: /**
176: * Returns the insert SQL.
177: *
178: * @return the inser SQL
179: */
180: public String buildInsertSQL() {
181: StringBuilder insertSQL = new StringBuilder();
182: StringBuilder questionMarks = new StringBuilder();
183: insertSQL.append("INSERT INTO ").append(getTableName()).append(
184: " (");
185: int counter = -1;
186: for (String stringProperty : this .propertyMapping) {
187: final Property property = this .getProperty(stringProperty);
188: // persistent field info
189: final PersistentPropertyInfo info = this
190: .getPersistentFieldInfo(stringProperty);
191:
192: insertSQL
193: .append((info.getDbName().length() == 0) ? property
194: .getName() : info.getDbName());
195: questionMarks.append("?");
196: counter++;
197: // store the property
198: this .propertyInfo[counter] = property;
199: if (counter + 1 < this .propertyMapping.length) {
200: insertSQL.append(", ");
201: questionMarks.append(", ");
202: }
203: }
204: insertSQL.append(") ").append("VALUES (").append(
205: questionMarks.toString()).append(")");
206:
207: return insertSQL.toString();
208: }
209:
210: private String getTableName() {
211: if (useSchemaName && this .introspector.hasSchema()) {
212: return this .introspector.getShemaName() + "."
213: + this .introspector.getTableName();
214: }
215: return this .introspector.getTableName();
216: }
217:
218: private String getClassName() {
219: return this .introspector.getPersistentClassName();
220: }
221:
222: @SuppressWarnings("unchecked")
223: private Property getProperty(String property) {
224: Property info = null;
225: if (this .introspector.hasEmbeddedPKClass()) {
226: final EmbeddedClassIntrospector pkintro = this .introspector
227: .getEmbeddedPKClass();
228: final List<Property> pkFields = pkintro
229: .getPersitentProperties();
230:
231: for (Property current : pkFields) {
232: if (current.getName().equals(property)) {
233: info = current;
234: break;
235: }
236: }
237: }
238: if (info == null) {
239: for (Property current : this .introspector
240: .getPersitentProperties()) {
241: if (current.getName().equals(property)) {
242: info = current;
243: break;
244: }
245: }
246:
247: if (info == null) {
248: throw new IllegalArgumentException("The property ("
249: + property + ") is not a persistent field");
250: }
251: }
252:
253: return info;
254: }
255:
256: @SuppressWarnings("unchecked")
257: private PersistentPropertyInfo getPersistentFieldInfo(
258: String property) {
259: PersistentPropertyInfo info = null;
260: if (this .introspector.hasEmbeddedPKClass()) {
261: final EmbeddedClassIntrospector pkintro = this .introspector
262: .getEmbeddedPKClass();
263: final List<Property> pkFields = pkintro
264: .getPersitentProperties();
265:
266: for (Property current : pkFields) {
267: if (current.getName().equals(property)) {
268: info = pkintro.getPresistentFieldInfo(current);
269: break;
270: }
271: }
272: }
273: if (info == null) {
274: for (Property current : this .introspector
275: .getPersitentProperties()) {
276: if (current.getName().equals(property)) {
277: info = this .introspector
278: .getPresistentFieldInfo(current);
279: break;
280: }
281: }
282:
283: if (info == null) {
284: throw new IllegalArgumentException("The property ("
285: + property + ") is not a persistent field");
286: }
287: }
288:
289: return info;
290: }
291:
292: /**
293: * Creates the data.
294: *
295: * @author Daniel Wiese
296: * @since 17.04.2006
297: * @see com.bm.testsuite.dataloader.InitialDataSet#create()
298: */
299: public void create() {
300: BasicDataSource ds = new BasicDataSource(Ejb3UnitCfg
301: .getConfiguration());
302: Connection con = null;
303: PreparedStatement prep = null;
304: try {
305: con = ds.getConnection();
306: prep = con.prepareStatement(this .insertSQLString);
307: final CSVParser parser = new CSVParser(getCSVInputStream());
308: parser.setCommentStart("#;!");
309: parser.setEscapes("nrtf", "\n\r\t\f");
310: String value;
311: int count = 0;
312: int lastLineNumber = parser.lastLineNumber();
313: while ((value = parser.nextValue()) != null) {
314: if (parser.lastLineNumber() != lastLineNumber) {
315: // we have a new line
316: lastLineNumber = parser.lastLineNumber();
317: count = 0;
318: }
319:
320: // insert only if neccessary (ignore not requiered fields)
321: if (count < this .propertyInfo.length) {
322: this .setPreparedStatement(count + 1, prep,
323: this .propertyInfo[count], value);
324: count++;
325: }
326:
327: // execute sql
328: if (count == this .propertyInfo.length) {
329: prep.execute();
330: }
331:
332: }
333:
334: parser.close();
335: } catch (FileNotFoundException e) {
336: log.error("Data-Loader failing ", e);
337: new RuntimeException(e);
338: } catch (IOException e) {
339: log.error("Data-Loader failing ", e);
340: new RuntimeException(e);
341: } catch (SQLException e) {
342: log.error("Data-Loader failing ", e);
343: new RuntimeException(e);
344: } finally {
345: SQLUtils.cleanup(con, prep);
346: }
347: }
348:
349: private InputStream getCSVInputStream() {
350: InputStream toReturn = null;
351: if (this .isCopressed) {
352: try {
353: toReturn = Ejb3Utils.unjar(new FileInputStream(
354: this .file));
355: if (toReturn == null) {
356: throw new IllegalArgumentException(
357: "The copressed file " + this .file.getName()
358: + " was empty");
359: }
360: } catch (IOException e) {
361: log.error("The file " + this .file.getName()
362: + " could not be accessed", e);
363: throw new IllegalArgumentException("The file "
364: + this .file.getAbsolutePath()
365: + " could not be accessed", e);
366: }
367:
368: } else {
369: try {
370: toReturn = new FileInputStream(this .file);
371: } catch (FileNotFoundException e) {
372: throw new IllegalArgumentException(
373: "The copressed file " + this .file.getName()
374: + "not found");
375: }
376: }
377:
378: return toReturn;
379: }
380:
381: /**
382: * Deletes the data.
383: *
384: * @param em -
385: * the entyty manager.
386: * @author Daniel Wiese
387: * @since 17.04.2006
388: * @see com.bm.testsuite.dataloader.InitialDataSet#cleanup(EntityManager)
389: */
390: public void cleanup(EntityManager em) {
391: StringBuilder deleteSQL = new StringBuilder();
392: deleteSQL.append("DELETE FROM ").append(getClassName());
393: EntityTransaction tx = em.getTransaction();
394: tx.begin();
395: Query query = em.createQuery(deleteSQL.toString());
396: query.executeUpdate();
397: tx.commit();
398:
399: }
400:
401: /**
402: * Sets the value (using the right type) in the prepared statement.
403: *
404: * @param index -
405: * the index inside the premared statement
406: * @param statement -
407: * the prepared statement itself
408: * @param prop -
409: * the property representing the type
410: * @param value -
411: * the value to be set
412: * @throws SQLException -
413: * in error case
414: */
415: private void setPreparedStatement(int index,
416: PreparedStatement statement, Property prop, String value)
417: throws SQLException {
418: // convert to nonprimitive if primitive
419: Class type = Ejb3Utils.getNonPrimitiveType(prop.getType());
420:
421: if (type.equals(String.class)) {
422: statement.setString(index, value);
423: } else if (type.equals(Integer.class)) {
424: statement.setInt(index, ((value.equals("")) ? 0 : Integer
425: .valueOf(value)));
426: } else if (type.equals(Long.class)) {
427: statement.setLong(index, ((value.equals("")) ? 0 : Long
428: .valueOf(value)));
429: } else if (type.equals(Boolean.class)) {
430: final boolean result = (value != null
431: && value.equals("True") ? true : false);
432: statement.setBoolean(index, result);
433: } else if (type.equals(Short.class)) {
434: statement.setShort(index, ((value.equals("")) ? 0 : Short
435: .valueOf(value)));
436: } else if (type.equals(Byte.class)) {
437: statement.setByte(index, Byte.valueOf(value));
438: } else if (type.equals(Character.class)) {
439: statement.setString(index, String.valueOf(value));
440: } else if (type.equals(Date.class)) {
441: parseAndSetDate(index, value, statement);
442: } else if (type.equals(Double.class)) {
443: statement.setDouble(index, ((value.equals("")) ? 0 : Double
444: .valueOf(value)));
445: } else if (type.equals(Float.class)) {
446: statement.setFloat(index, ((value.equals("")) ? 0 : Float
447: .valueOf(value)));
448: } else if (type.isEnum()) {
449: // TODO: possible to have the ordinal value of an
450: // enum in a .csv file maybe it would be reasonable to extend this
451: // so that it is also possible to have enums by literal name
452: statement.setInt(index, Integer.valueOf(value));
453: }
454:
455: }
456:
457: private void parseAndSetDate(int index, String value,
458: PreparedStatement statement) throws SQLException {
459: if (value.equals("") || value.equalsIgnoreCase("null")) {
460: statement.setNull(index, java.sql.Types.DATE);
461: } else {
462: // try to guess the default format
463: boolean success = false;
464: List<DateFormats> allValidFormats = getValidFormats();
465: for (DateFormats current : allValidFormats) {
466: try {
467: current.parseToPreparedStatemnt(value, statement,
468: index);
469: success = true;
470: break;
471: } catch (ParseException ex) {
472: log.debug("Date parser (" + current
473: + ") was not working, trying next");
474: }
475: }
476:
477: if (!success) {
478: // java 1.5 will convert this to string builder internally
479: String msg = "Illegal date format (" + value + ")";
480: msg += " expecting one of: ";
481: for (DateFormats current : DateFormats.values()) {
482: msg += current.toPattern() + ", ";
483: }
484:
485: throw new IllegalArgumentException(msg);
486: }
487: }
488: }
489:
490: private List<DateFormats> getValidFormats() {
491: final List<DateFormats> dtFormats = new ArrayList<DateFormats>();
492: if (!this.userDefinedDateFormats.isEmpty()) {
493: dtFormats.addAll(this.userDefinedDateFormats);
494: }
495:
496: dtFormats.addAll(Arrays.asList(DateFormats.systemValues()));
497: return dtFormats;
498: }
499:
500: }
|