View Javadoc

1   /*
2    * org.riverock.dbrevision - Database revision engine
3    * For more information about DbRevision, please visit project site
4    * http://www.riverock.org
5    *
6    * Copyright (C) 2006-2006, Riverock Software, All Rights Reserved.
7    *
8    * Riverock - The Open-source Java Development Community
9    * http://www.riverock.org
10   *
11   *
12   * This library is free software; you can redistribute it and/or
13   * modify it under the terms of the GNU Lesser General Public
14   * License as published by the Free Software Foundation; either
15   * version 2.1 of the License, or (at your option) any later version.
16   *
17   * This library is distributed in the hope that it will be useful,
18   * but WITHOUT ANY WARRANTY; without even the implied warranty of
19   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20   * Lesser General Public License for more details.
21   *
22   * You should have received a copy of the GNU Lesser General Public
23   * License along with this library; if not, write to the Free Software
24   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
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   * User: Admin
48   * Date: May 15, 2003
49   * Time: 11:15:35 PM
50   * <p/>
51   * $Id: PatchService.java 1141 2006-12-14 14:43:29Z serg_main $
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                                     seq.setCacheSize(getInteger(action, "sequence_cache_size", 0));
461                                     seq.setIncrementBy(getInteger(action, "sequence_increment", 1));
462                                     seq.setIsCycle(getBoolean(action, "sequence_is_cycle", false));
463                                     seq.setIsOrder(getBoolean(action, "sequence_is_order", false));
464                                     seq.setLastNumber(getLong(action, "sequence_last_number", 0));
465                                     seq.setMaxValue(getString(action, "sequence_max_value", "0"));
466                                     seq.setMinValue(getInteger(action, "sequence_min_value", 0));
467                                     seq.setName(getString(action, "sequence_name"));
468 
469                                     db_.createSequence(seq);
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                             //noinspection UnusedAssignment
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                                     String nameTable = getString(action, "name_table");
544                                     if (nameTable != null) {
545                                         db_.dropTable(nameTable);
546                                         db_.getConnection().commit();
547                                     }
548                                     else {
549                                         log.error("Patch - " + patch.getName() + ", action '" + DROP_TABLE_TYPE + "' must have parameter 'name_table'");
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                                     String nameSeq = getString(action, "name_sequence");
566                                     if (nameSeq != null) {
567                                         db_.dropSequence(nameSeq);
568                                     }
569                                     else {
570                                         log.error("Patch - " + patch.getName() + ", action '" + DROP_TABLE_TYPE + "' must have parameter 'name_sequence'");
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 }