1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 package org.riverock.dbrevision.manager.patch;
27
28 import java.sql.Statement;
29 import java.sql.SQLException;
30 import java.util.HashMap;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.ArrayList;
34
35 import org.apache.log4j.Logger;
36 import org.apache.commons.lang.StringUtils;
37
38 import org.riverock.dbrevision.annotation.schema.db.*;
39 import org.riverock.dbrevision.db.Database;
40 import org.riverock.dbrevision.db.DatabaseManager;
41 import org.riverock.dbrevision.db.DatabaseStructureManager;
42 import org.riverock.dbrevision.exception.DbRevisionException;
43 import org.riverock.dbrevision.utils.DbUtils;
44 import org.riverock.dbrevision.utils.Utils;
45
46
47
48
49
50
51
52
53 public final class PatchService {
54 private static Logger log = Logger.getLogger(PatchService.class);
55
56 static final String CUSTOM_SQL_TYPE = "CUSTOM_SQL";
57 static final String CUSTOM_CLASS_ACTION_TYPE = "CUSTOM_CLASS_ACTION";
58 static final String CREATE_SEQUENCE_TYPE = "CREATE_SEQUENCE";
59 static final String CREATE_TABLE_TYPE = "CREATE_TABLE";
60 static final String ADD_TABLE_COLUMN_TYPE = "ADD_TABLE_COLUMN";
61 static final String DROP_TABLE_COLUMN_TYPE = "DROP_TABLE_COLUMN";
62 static final String ADD_PRIMARY_KEY_TYPE = "ADD_PRIMARY_KEY";
63 static final String ADD_FOREIGN_KEY_TYPE = "ADD_FOREIGN_KEY";
64 static final String DROP_PRIMARY_KEY_TYPE = "DROP_PRIMARY_KEY";
65 static final String DROP_FOREIGN_KEY_TYPE = "DROP_FOREIGN_KEY";
66 static final String DROP_TABLE_TYPE = "DROP_TABLE";
67 static final String DROP_SEQUENCE_TYPE = "DROP_SEQUENCE";
68 static final String DELETE_BEFORE_FK_TYPE = "DELETE_BEFORE_FK";
69 static final String COPY_COLUMN_TYPE = "COPY_COLUMN";
70 static final String CLONE_COLUMN_TYPE = "CLONE_COLUMN";
71 static final String COPY_TABLE_TYPE = "COPY_TABLE";
72
73 private static enum ActionTypes {
74 SQL,
75 CUSTOM_CLASS,
76 ADD_TABLE_FIELD,
77
78 CREATE_SEQUENCE,
79 CREATE_TABLE,
80 DROP_TABLE_COLUMN,
81 ADD_PRIMARY_KEY,
82 ADD_FOREIGN_KEY,
83 DROP_PRIMARY_KEY,
84 DROP_FOREIGN_KEY,
85 DROP_TABLE,
86 DROP_SEQUENCE,
87 DELETE_BEFORE_FK,
88 COPY_COLUMN,
89 CLONE_COLUMN,
90 COPY_TABLE
91 }
92
93 private static Map<String, ActionTypes> actionTypes = new HashMap<String, ActionTypes>();
94
95 static {
96 actionTypes.put(CustomClassAction.class.getName(), ActionTypes.SQL);
97 actionTypes.put(AddTableFieldAction.class.getName(), ActionTypes.ADD_TABLE_FIELD);
98 actionTypes.put(CustomClassAction.class.getName(), ActionTypes.CUSTOM_CLASS);
99
100 actionTypes.put(CREATE_SEQUENCE_TYPE, ActionTypes.CREATE_SEQUENCE);
101 actionTypes.put(CREATE_TABLE_TYPE, ActionTypes.CREATE_TABLE);
102 actionTypes.put(DROP_TABLE_COLUMN_TYPE, ActionTypes.DROP_TABLE_COLUMN);
103 actionTypes.put(ADD_PRIMARY_KEY_TYPE, ActionTypes.ADD_PRIMARY_KEY);
104 actionTypes.put(ADD_FOREIGN_KEY_TYPE, ActionTypes.ADD_FOREIGN_KEY);
105 actionTypes.put(DROP_PRIMARY_KEY_TYPE, ActionTypes.DROP_PRIMARY_KEY);
106 actionTypes.put(DROP_FOREIGN_KEY_TYPE, ActionTypes.DROP_FOREIGN_KEY);
107 actionTypes.put(DROP_TABLE_TYPE, ActionTypes.DROP_TABLE);
108 actionTypes.put(DROP_SEQUENCE_TYPE, ActionTypes.DROP_SEQUENCE);
109 actionTypes.put(DELETE_BEFORE_FK_TYPE, ActionTypes.DELETE_BEFORE_FK);
110 actionTypes.put(COPY_COLUMN_TYPE, ActionTypes.COPY_COLUMN);
111 actionTypes.put(CLONE_COLUMN_TYPE, ActionTypes.CLONE_COLUMN);
112 actionTypes.put(COPY_TABLE_TYPE, ActionTypes.COPY_TABLE);
113 }
114
115 public static void processPatch(Database database, Patch patch) {
116 if (patch == null) {
117 throw new NullPointerException("patch is null");
118 }
119 if (log.isInfoEnabled()) {
120 log.info("process definition " + patch.getName());
121 }
122
123 validate(database, patch);
124 processTable(database, patch);
125 processPrimaryKey(database, patch);
126 processForeignKeys(database, patch);
127 processSequences(database, patch);
128 processAction(database, patch);
129 }
130
131 public static String getString(Action action, String nameParam, String defValue) {
132 String value = getString(action, nameParam);
133 if (value == null) {
134 return defValue;
135 }
136
137 return value;
138 }
139
140 public static String getString(Action action, String nameParam) {
141 if (action == null || nameParam == null) {
142 return null;
143 }
144
145 for (ActionParameter actionParameter : action.getActionParameters()) {
146 if (actionParameter.getName().equals(nameParam)) {
147 return actionParameter.getData();
148 }
149 }
150 return null;
151 }
152
153 public static Double getDouble(List<ActionParameter> actionList, String nameParam, double defValue) {
154 Double value = getDouble(actionList, nameParam);
155 if (value == null) {
156 return defValue;
157 }
158
159 return value;
160 }
161
162 public synchronized static Double getDouble(List<ActionParameter> actionList, String nameParam) {
163 if (actionList == null || nameParam == null || nameParam.length() == 0) {
164 return null;
165 }
166
167 for (ActionParameter action : actionList) {
168 if (action.getName().equals(nameParam)) {
169 String value = action.getData();
170 Double doubleValue;
171 try {
172 doubleValue = new Double(value);
173 }
174 catch (Exception e) {
175 String errorString = "Error convert String to Double from data - " + action.getData();
176 log.error(errorString, e);
177 throw new IllegalArgumentException(errorString, e);
178 }
179 return doubleValue;
180 }
181 }
182 return null;
183 }
184
185 public static Long getLong(Action action, String nameParam, long defValue) {
186 Long value = getLong(action, nameParam);
187 if (value == null) {
188 return defValue;
189 }
190
191 return value;
192 }
193
194 public synchronized static Long getLong(Action action, String nameParam) {
195 if (action == null || nameParam == null) {
196 return null;
197 }
198
199 for (ActionParameter actionParameter : action.getActionParameters()) {
200 if (actionParameter.getName().equals(nameParam)) {
201 String value = actionParameter.getData();
202 Long longValue;
203 try {
204 longValue = new Long(value);
205 }
206 catch (Exception e) {
207 String errorString = "Error convert String to Long from data - " + actionParameter.getData();
208 log.error(errorString, e);
209 throw new IllegalArgumentException(errorString, e);
210 }
211 return longValue;
212 }
213 }
214 return null;
215 }
216
217 public static Integer getInteger(Action action, String nameParam, int defValue) {
218 Integer value = getInteger(action, nameParam);
219 if (value == null) {
220 return defValue;
221 }
222
223 return value;
224 }
225
226 public static Integer getInteger(Action action, String nameParam) {
227 if (action == null || nameParam == null) {
228 return null;
229 }
230
231 for (ActionParameter actionParameter : action.getActionParameters()) {
232 if (actionParameter.getName().equals(nameParam)) {
233 String value = actionParameter.getData();
234 Integer intValue;
235 try {
236 intValue = new Integer(value);
237 }
238 catch (Exception e) {
239 String errorString = "Error convert String to Integer from data - " + actionParameter.getData();
240 log.error(errorString, e);
241 throw new IllegalArgumentException(errorString, e);
242 }
243 return intValue;
244 }
245 }
246 return null;
247 }
248
249 public static Boolean getBoolean(Action action, String nameParam, boolean defValue) {
250 Boolean value = getBoolean(action, nameParam);
251 if (value == null) {
252 return defValue;
253 }
254
255 return value;
256 }
257
258 public synchronized static Boolean getBoolean(Action action, String nameParam) {
259 if (action == null || nameParam == null) {
260 return null;
261 }
262
263 for (ActionParameter actionParameter : action.getActionParameters()) {
264 if (actionParameter.getName().equals(nameParam)) {
265 String value = actionParameter.getData();
266 if (value == null) {
267 value = "false";
268 }
269
270 if (value.equals("1")) {
271 value = "true";
272 }
273
274 Boolean booleanValue;
275 try {
276 booleanValue = Boolean.valueOf(value);
277 }
278 catch (Exception e) {
279 String errorString = "Error convert String to Boolean from data - " + actionParameter.getData();
280 log.error(errorString, e);
281 throw new IllegalArgumentException(errorString);
282 }
283 return booleanValue;
284 }
285 }
286 return null;
287 }
288
289
290
291 private static void validate(Database database, Patch patch) {
292 log.debug("processTable ");
293 if (patch == null) {
294 return;
295 }
296
297 for (Object o : patch.getActionOrCustomClassActionOrSqlAction()) {
298 if (o instanceof Validator) {
299 Validator obj = (Validator) o;
300 String className = obj.getClazz();
301 if (StringUtils.isBlank(className)) {
302 throw new DbRevisionException("Patch: " + patch.getName() + ", class name is blank");
303 }
304
305 PatchStatus status;
306 try {
307 Object object = Utils.createCustomObject(className);
308 if (object == null) {
309 throw new DbRevisionException("Class '" + className + "', object is null");
310 }
311
312 status = ((PatchValidator) object).validate(database);
313 }
314 catch (Exception e) {
315 throw new DbRevisionException("Error process class '" + className + "'");
316 }
317 if (status!=null && status.getStatus()== PatchStatus.Status.ERROR) {
318 throw new DbRevisionException("Error process class '" + className + "'");
319 }
320 }
321 }
322 }
323
324 private static void processTable(Database database, Patch patch) {
325 log.debug("processTable ");
326 if (patch == null) {
327 return;
328 }
329
330 for (Object o : patch.getActionOrCustomClassActionOrSqlAction()) {
331 if (o instanceof DbTable) {
332 database.createTable((DbTable) o);
333 }
334 }
335 }
336
337 private static void processPrimaryKey(Database database, Patch patch) {
338 log.debug("processPrimaryKey ");
339 if (patch == null) {
340 return;
341 }
342
343 for (Object o : patch.getActionOrCustomClassActionOrSqlAction()) {
344 if (o instanceof DbPrimaryKey) {
345 DbPrimaryKey pk = (DbPrimaryKey) o;
346
347 if (!pk.getColumns().isEmpty()) {
348 DbSchema schema = DatabaseManager.getDbStructure(database);
349 DbTable table = DatabaseManager.getTableFromStructure(schema, pk.getTableName());
350 DatabaseManager.addPrimaryKey(database, table, pk);
351 }
352 }
353 }
354 }
355
356 private static void processForeignKeys(Database adapter, Patch patch) {
357 log.debug("processForeignKeys ");
358 if (patch == null) {
359 return;
360 }
361
362 List<DbForeignKey> keys = new ArrayList<DbForeignKey>();
363 for (Object o : patch.getActionOrCustomClassActionOrSqlAction()) {
364 if (o instanceof DbForeignKey) {
365 keys.add((DbForeignKey) o);
366 }
367 }
368
369 int p = 0;
370 for (DbForeignKey key : keys) {
371 if (StringUtils.isBlank(key.getFkName())) {
372 key.setFkName(key.getFkTableName() + p + "_fk");
373 }
374 DatabaseStructureManager.createForeignKey(adapter, key);
375 }
376 }
377
378 private static void processSequences(Database db_, Patch patch) {
379 log.debug("processSequences ");
380 if (patch == null) {
381 return;
382 }
383
384 for (Object o : patch.getActionOrCustomClassActionOrSqlAction()) {
385 if (o instanceof DbSequence) {
386 db_.createSequence((DbSequence) o);
387 }
388 }
389 }
390
391 private static void processAction(Database db_, Patch patch) {
392 log.debug("processAction ");
393 if (patch == null) {
394 return;
395 }
396
397 for (Object o : patch.getActionOrCustomClassActionOrSqlAction()) {
398 ActionTypes type = actionTypes.get(o.getClass().getName());
399 if (type != null) {
400 switch (type) {
401 case ADD_FOREIGN_KEY: {
402
403 }
404 break;
405
406 case ADD_PRIMARY_KEY: {
407
408 }
409 break;
410
411 case ADD_TABLE_FIELD: {
412 if (log.isDebugEnabled()) {
413 log.debug("process action ADD_TABLE_COLUMN_TYPE");
414 }
415 AddTableFieldAction obj = (AddTableFieldAction) o;
416 DbField field = obj.getField();
417 int fieldType = DatabaseManager.sqlTypesMapping(field.getDataType());
418 if (log.isDebugEnabled()) {
419 log.debug("field.getDataType(): " + field.getDataType() +", fieldType: " + fieldType);
420 }
421 field.setJavaType(fieldType);
422
423 try {
424 DatabaseStructureManager.addColumn(db_, obj.getTableName(), field);
425 }
426 catch (Throwable e) {
427 log.error("Error add column");
428 log.error(" table name; " + obj.getTableName());
429 log.error(" applType; "+ obj.getField().getApplType());
430 log.error(" comment; "+ obj.getField().getComment());
431 log.error(" dataType: "+ obj.getField().getDataType());
432 log.error(" decimalDigit: "+ obj.getField().getDecimalDigit());
433 log.error(" defaultValue: "+ obj.getField().getDefaultValue());
434 log.error(" JavaStringType: "+ obj.getField().getJavaStringType());
435 log.error(" JavaType; "+ obj.getField().getJavaType());
436 log.error(" name: "+ obj.getField().getName());
437 log.error(" nullable: "+ obj.getField().getNullable());
438 log.error(" size: "+ obj.getField().getSize());
439 throw new DbRevisionException(e);
440 }
441 }
442 break;
443
444 case CLONE_COLUMN: {
445
446 }
447 break;
448
449 case COPY_COLUMN: {
450
451 }
452 break;
453
454 case CREATE_SEQUENCE: {
455 DbSequence seq = new DbSequence();
456 if (true) {
457 throw new RuntimeException("not impleented");
458 }
459
460
461
462
463
464
465
466
467
468
469
470
471 }
472 break;
473
474 case CREATE_TABLE: {
475
476 }
477 break;
478
479 case CUSTOM_CLASS: {
480 CustomClassAction obj = (CustomClassAction) o;
481 String className = obj.getClazz();
482 if (className == null) {
483 throw new DbRevisionException("Patch - " + patch.getName() + ", action '" + CUSTOM_CLASS_ACTION_TYPE + "', class name is null");
484 }
485
486 try {
487 Object object = Utils.createCustomObject(className);
488 if (object == null) {
489 throw new DbRevisionException("Class '" + className + "', object is null");
490 }
491
492 ((PatchAction) object).process(db_);
493 }
494 catch (Exception e) {
495 throw new DbRevisionException("Error process class '" + className + "'", e);
496 }
497 }
498 break;
499
500 case SQL: {
501 SqlAction obj = (SqlAction) o;
502 String sql = obj.getSql();
503 if (log.isDebugEnabled()) {
504 log.debug("Custom sql " + sql);
505 }
506 Statement st = null;
507 try {
508 st = db_.getConnection().createStatement();
509 st.execute(sql);
510 }
511 catch (SQLException e) {
512 log.error("SQL:\n" + sql);
513 throw new DbRevisionException(e);
514 }
515 finally {
516 DbUtils.close(st);
517
518 st = null;
519 }
520 }
521 break;
522
523 case DELETE_BEFORE_FK: {
524
525 }
526 break;
527
528 case DROP_FOREIGN_KEY: {
529
530 }
531 break;
532
533 case DROP_PRIMARY_KEY: {
534
535 }
536 break;
537
538 case DROP_TABLE: {
539 if (true) {
540 throw new RuntimeException("not impleented");
541 }
542
543
544
545
546
547
548
549
550
551
552 }
553 break;
554
555 case DROP_TABLE_COLUMN: {
556
557 }
558 break;
559
560 case DROP_SEQUENCE: {
561 if (true) {
562 throw new RuntimeException("not impleented");
563 }
564
565
566
567
568
569
570
571
572
573 }
574 break;
575 default:
576 String errorString = "missed action type: " + type;
577 log.error(errorString);
578 throw new DbRevisionException(errorString);
579
580 }
581 }
582 }
583 }
584 }