001: /* ***** BEGIN LICENSE BLOCK *****
002: * Version: MPL 1.1
003: * The contents of this file are subject to the Mozilla Public License Version
004: * 1.1 (the "License"); you may not use this file except in compliance with
005: * the License. You may obtain a copy of the License at
006: * http://www.mozilla.org/MPL/
007: *
008: * Software distributed under the License is distributed on an "AS IS" basis,
009: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
010: * for the specific language governing rights and limitations under the
011: * License.
012: *
013: * The Original Code is Riot.
014: *
015: * The Initial Developer of the Original Code is
016: * Neteye GmbH.
017: * Portions created by the Initial Developer are Copyright (C) 2006
018: * the Initial Developer. All Rights Reserved.
019: *
020: * Contributor(s):
021: * Felix Gnass [fgnass at neteye dot de]
022: *
023: * ***** END LICENSE BLOCK ***** */
024: package org.riotfamily.revolt;
025:
026: import java.util.Collection;
027: import java.util.HashMap;
028: import java.util.Iterator;
029: import java.util.Map;
030:
031: import javax.sql.DataSource;
032:
033: import org.apache.commons.logging.Log;
034: import org.apache.commons.logging.LogFactory;
035: import org.riotfamily.revolt.support.DatabaseUtils;
036: import org.riotfamily.revolt.support.LogTable;
037: import org.springframework.beans.factory.BeanFactoryUtils;
038: import org.springframework.context.ApplicationContext;
039: import org.springframework.context.ApplicationContextAware;
040: import org.springframework.util.StringUtils;
041:
042: /**
043: * @author Felix Gnass [fgnass at neteye dot de]
044: */
045: public class Evolver implements ApplicationContextAware {
046:
047: private static final Log log = LogFactory.getLog(Evolver.class);
048:
049: private boolean enabled = true;
050:
051: private boolean automatic;
052:
053: private HashMap scripts = new HashMap();
054:
055: private HashMap logTables = new HashMap();
056:
057: /**
058: * Sets whether Revolt should automatically apply pending refactorings.
059: */
060: public void setAutomatic(boolean automatic) {
061: this .automatic = automatic;
062: }
063:
064: /**
065: * Sets whether Revolt should validate/evolve the schema.
066: * Default is <code>true</code>.
067: */
068: public void setEnabled(boolean enabled) {
069: this .enabled = enabled;
070: }
071:
072: public void setApplicationContext(
073: ApplicationContext applicationContext) {
074: Collection evolutions = BeanFactoryUtils
075: .beansOfTypeIncludingAncestors(applicationContext,
076: EvolutionHistory.class).values();
077:
078: if (enabled) {
079: Iterator it = evolutions.iterator();
080:
081: while (it.hasNext()) {
082: EvolutionHistory history = (EvolutionHistory) it.next();
083: history.init(getLogTable(history));
084: getScript(history).append(history.getScript());
085: }
086:
087: if (automatic) {
088: executeScripts();
089: }
090:
091: String instructions = getInstructions();
092: if (StringUtils.hasLength(instructions)) {
093: throw new EvolutionInstructions(instructions);
094: }
095:
096: while (it.hasNext()) {
097: EvolutionHistory history = (EvolutionHistory) it.next();
098: history.validate();
099: }
100: }
101: }
102:
103: private LogTable getLogTable(EvolutionHistory history) {
104: DataSource dataSource = history.getDataSource();
105: LogTable logTable = (LogTable) logTables.get(dataSource);
106: if (logTable == null) {
107: logTable = new LogTable(dataSource, history.getDialect());
108: logTables.put(history.getDataSource(), logTable);
109: if (!logTable.exists()) {
110: log.info("Revolt log-table does not exist.");
111: getScript(history).append(
112: logTable.getCreateTableScript());
113: }
114: }
115: return logTable;
116: }
117:
118: private Script getScript(EvolutionHistory history) {
119: DataSource dataSource = history.getDataSource();
120: Script script = (Script) scripts.get(dataSource);
121: if (script == null) {
122: script = new Script();
123: scripts.put(dataSource, script);
124: }
125: return script;
126: }
127:
128: private void executeScripts() {
129: Iterator it = scripts.entrySet().iterator();
130: while (it.hasNext()) {
131: Map.Entry entry = (Map.Entry) it.next();
132: DataSource dataSource = (DataSource) entry.getKey();
133: Script script = (Script) entry.getValue();
134: if (!script.isManualExecutionOnly()) {
135: script.execute(dataSource);
136: }
137: }
138: }
139:
140: private String getInstructions() {
141: StringBuffer sb = new StringBuffer();
142: Iterator it = scripts.entrySet().iterator();
143: while (it.hasNext()) {
144: Map.Entry entry = (Map.Entry) it.next();
145: DataSource dataSource = (DataSource) entry.getKey();
146: Script script = (Script) entry.getValue();
147: if (!automatic || script.isManualExecutionOnly()) {
148: String sql = script.getSql();
149: if (StringUtils.hasLength(sql)) {
150: sb
151: .append("\n\n-------------------------------------------------------------------------\n\n");
152: sb.append("The database ").append(
153: DatabaseUtils.getUrl(dataSource));
154: sb
155: .append(" is not up-to-date.\nPlease execute the"
156: + " following SQL commands to evolve the schema:\n\n");
157:
158: sb.append(sql);
159: sb
160: .append("\n\n-------------------------------------------------------------------------\n\n");
161: }
162: }
163: }
164: return sb.toString();
165: }
166:
167: }
|