KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > riotfamily > revolt > EvolutionHistory


1 /* ***** BEGIN LICENSE BLOCK *****
2  * Version: MPL 1.1
3  * The contents of this file are subject to the Mozilla Public License Version
4  * 1.1 (the "License"); you may not use this file except in compliance with
5  * the License. You may obtain a copy of the License at
6  * http://www.mozilla.org/MPL/
7  *
8  * Software distributed under the License is distributed on an "AS IS" basis,
9  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
10  * for the specific language governing rights and limitations under the
11  * License.
12  *
13  * The Original Code is Riot.
14  *
15  * The Initial Developer of the Original Code is
16  * Neteye GmbH.
17  * Portions created by the Initial Developer are Copyright (C) 2006
18  * the Initial Developer. All Rights Reserved.
19  *
20  * Contributor(s):
21  * Felix Gnass [fgnass at neteye dot de]
22  *
23  * ***** END LICENSE BLOCK ***** */

24 package org.riotfamily.revolt;
25
26 import java.util.ArrayList JavaDoc;
27 import java.util.Iterator JavaDoc;
28 import java.util.List JavaDoc;
29
30 import javax.sql.DataSource JavaDoc;
31
32 import org.apache.commons.logging.Log;
33 import org.apache.commons.logging.LogFactory;
34 import org.riotfamily.revolt.definition.Database;
35 import org.riotfamily.revolt.support.DatabaseUtils;
36 import org.riotfamily.revolt.support.DialectResolver;
37 import org.riotfamily.revolt.support.LogTable;
38 import org.riotfamily.revolt.support.ScriptBuilder;
39 import org.springframework.beans.factory.BeanNameAware;
40
41 /**
42  * @author Felix Gnass [fgnass at neteye dot de]
43  *
44  */

45 public class EvolutionHistory implements BeanNameAware {
46
47     private static final Log log = LogFactory.getLog(EvolutionHistory.class);
48     
49     private String JavaDoc moduleName;
50     
51     private List JavaDoc changeSets;
52
53     private DataSource JavaDoc dataSource;
54     
55     private Dialect dialect;
56     
57     private LogTable logTable;
58     
59     private ArrayList JavaDoc appliedIds;
60     
61     private boolean newModule;
62     
63     public EvolutionHistory(DataSource JavaDoc dataSource) {
64         this.dataSource = dataSource;
65         this.dialect = new DialectResolver().getDialect(dataSource);
66     }
67     
68     public DataSource JavaDoc getDataSource() {
69         return this.dataSource;
70     }
71     
72     public Dialect getDialect() {
73         return this.dialect;
74     }
75
76     public void setBeanName(String JavaDoc name) {
77         this.moduleName = name;
78     }
79     
80     public String JavaDoc getModuleName() {
81         return this.moduleName;
82     }
83
84     public void setChangeSets(ChangeSet[] changeSets) {
85         this.changeSets = new ArrayList JavaDoc();
86         for (int i = 0; i < changeSets.length; i++) {
87             ChangeSet changeSet = changeSets[i];
88             changeSet.setHistory(this);
89             changeSet.setSequenceNumber(i);
90             this.changeSets.add(changeSet);
91         }
92     }
93
94     public void validate() {
95         DatabaseUtils.validate(dataSource, evolveModel());
96     }
97     
98     private Database evolveModel() {
99         Database model = new Database();
100         Iterator JavaDoc it = changeSets.iterator();
101         while (it.hasNext()) {
102             ChangeSet changeSet = (ChangeSet) it.next();
103             changeSet.alterModel(model);
104         }
105         return model;
106     }
107     
108     /**
109      * Initializes the history from the given log-table.
110      */

111     public void init(LogTable logTable) {
112         this.logTable = logTable;
113         appliedIds = new ArrayList JavaDoc();
114         appliedIds.addAll(logTable.getAppliedChangeSetIds(moduleName));
115     }
116
117     /**
118      * Returns a script that needs to be executed in order update the schema.
119      */

120     public Script getScript() {
121         if (appliedIds.isEmpty()) {
122             log.info("The log-table contains no entries for module '"
123                     + moduleName + "'. Checking if schema is up-to-date ...");
124             
125             newModule = true;
126             return getEvolvedSetupScript();
127         }
128         else {
129             return getMigrationScript();
130         }
131     }
132     
133     private Script getEvolvedSetupScript() {
134         Script script = new Script();
135         Database model = evolveModel();
136         try {
137             DatabaseUtils.validate(dataSource, model);
138             log.info("Schema looks okay. Marking all changes as applied.");
139         }
140         catch (DatabaseOutOfSyncException e) {
141             log.info(e.getMessage());
142             log.info("Generating setup script ...");
143             ScriptBuilder builder = new ScriptBuilder(model, dialect);
144             script.append(builder.buildScript());
145         }
146         Iterator JavaDoc it = changeSets.iterator();
147         while (it.hasNext()) {
148             ChangeSet changeSet = (ChangeSet) it.next();
149             script.append(markAsApplied(changeSet));
150         }
151         return script;
152     }
153     
154     private Script getMigrationScript() {
155         Script script = new Script();
156         Iterator JavaDoc it = changeSets.iterator();
157         while (it.hasNext()) {
158             ChangeSet changeSet = (ChangeSet) it.next();
159             if (!isApplied(changeSet)) {
160                 script.append(changeSet.getScript(dialect));
161                 script.append(markAsApplied(changeSet));
162             }
163         }
164         return script;
165     }
166     
167     /**
168      * Returns whether the module is new or whether any entries previously
169      * existed in the log-table.
170      */

171     public boolean isNewModule() {
172         return newModule;
173     }
174     
175     /**
176      * Returns whether the given changeSet has already been applied.
177      * @throws DatabaseOutOfSyncException If the ChangeSet id in the log-table
178      * doesn't match the the one of the ChangeSet
179      */

180     private boolean isApplied(ChangeSet changeSet) {
181         if (appliedIds.size() > changeSet.getSequenceNumber()) {
182             String JavaDoc appliedId = (String JavaDoc) appliedIds.get(
183                     changeSet.getSequenceNumber());
184             
185             if (appliedId.equals(changeSet.getId())) {
186                 return true;
187             }
188             throw new DatabaseOutOfSyncException("ChangeSet number "
189                     + changeSet.getSequenceNumber() + " should be ["
190                     + changeSet.getId() + "] but is [" + appliedId + "]");
191         }
192         return false;
193     }
194     
195     /**
196      * Returns a script that can be used to add an entry to the log-table that
197      * marks the given ChangeSet as applied.
198      * @throws DatabaseOutOfSyncException If the sequence number of the
199      * ChangeSet doesn't match the number of already applied changes.
200      */

201     private Script markAsApplied(ChangeSet changeSet) {
202         if (changeSet.getSequenceNumber() != appliedIds.size()) {
203             throw new DatabaseOutOfSyncException("ChangeSet ["
204                     + changeSet.getId() + "] is number "
205                     + changeSet.getSequenceNumber() + " but there are already "
206                     + appliedIds.size() + " ChangeSets applied!");
207         }
208         appliedIds.add(changeSet.getId());
209         return logTable.getInsertScript(changeSet);
210     }
211         
212 }
213
Popular Tags