
org.ow2.bonita.util.DbMigration Maven / Gradle / Ivy
/**
* Copyright (C) 2010-2012 BonitaSoft S.A.
* BonitaSoft, 31 rue Gustave Eiffel - 38000 Grenoble
* This library is free software; you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Foundation
* version 2.1 of the License.
* This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
* Floor, Boston, MA 02110-1301, USA.
**/
package org.ow2.bonita.util;
import java.io.InputStream;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.transform.Transformers;
import org.ow2.bonita.env.EnvConstants;
import org.ow2.bonita.facade.uuid.ProcessInstanceUUID;
import org.ow2.bonita.runtime.event.EventConstants;
import org.ow2.bonita.runtime.event.EventCouple;
import org.ow2.bonita.runtime.event.IncomingEventInstance;
import org.ow2.bonita.runtime.event.Job;
import org.ow2.bonita.runtime.event.JobBuilder;
import org.ow2.bonita.runtime.event.OutgoingEventInstance;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Anthony Birembaut, Matthieu Chaffotte
*/
public final class DbMigration {
// FIXME check job name
private static final Logger LOG = LoggerFactory.getLogger(DbMigration.class);
private DbMigration() {
}
public static void main(final String[] args) throws Exception {
if (args == null || args.length != 3) {
final String message = ExceptionManager.getInstance().getFullMessage("bh_DBM_1");
throw new IllegalArgumentException(message);
}
// Check that bonita home is set
BonitaConstants.getBonitaHomeFolder();
final String domain = args[0];
final String db = args[1].toLowerCase();
final int stage = Integer.valueOf(args[2]);
final String useSearch = "bonita.search.use";
System.setProperty(useSearch, "false");
LOG.info("Starting Migration on tenant: " + domain);
try {
if (stage < 2) {
LOG.info("Stage 1: Updating history database schema...");
preUpdateDatabase(domain, EnvConstants.HB_CONFIG_HISTORY, db);
LOG.info("Stage 1: DONE");
}
if (stage < 3) {
LOG.info("Stage 2: Updating journal database schema...");
preUpdateDatabase(domain, EnvConstants.HB_CONFIG_CORE, db);
LOG.info("Stage 2: DONE");
}
if (stage < 4) {
LOG.info("Stage 3.1: Migrating asynchronous events...");
migrateAsyncEvents(domain, EnvConstants.HB_CONFIG_CORE);
LOG.info("Stage 3.1: DONE");
LOG.info("Stage 3.2: Migrating timer events...");
migrateTimerEvents(domain, EnvConstants.HB_CONFIG_CORE);
LOG.info("Stage 3.2: DONE");
LOG.info("Stage 3.3: Migrating deadline events...");
migrateDeadlineEvents(domain, EnvConstants.HB_CONFIG_CORE);
LOG.info("Stage 3.3: DONE");
LOG.info("Stage 3.4: Migrating error events...");
migrateErrorEvents(domain, EnvConstants.HB_CONFIG_CORE);
LOG.info("Stage 3.4: DONE");
LOG.info("Stage 3.5: Migrating signal events...");
migrateSignalEvents(domain, EnvConstants.HB_CONFIG_CORE);
LOG.info("Stage 3.5: DONE");
}
if (stage < 5) {
LOG.info("Stage 4: Cleaning history database...");
cleanDatabase(domain, EnvConstants.HB_CONFIG_HISTORY, db);
postUpdateDatabase(domain, EnvConstants.HB_CONFIG_HISTORY, db);
LOG.info("Stage 4: DONE");
}
if (stage < 6) {
LOG.info("Stage 5: Cleaning journal database...");
cleanDatabase(domain, EnvConstants.HB_CONFIG_CORE, db);
postUpdateDatabase(domain, EnvConstants.HB_CONFIG_CORE, db);
LOG.info("Stage 5: DONE");
}
LOG.info("Migration on tenant " + domain + ": DONE");
} finally {
System.clearProperty(useSearch);
}
}
private static void preUpdateDatabase(final String domain, final String configurationName, final String db)
throws Exception {
final String path = getSQLScriptPath(db, "pre");
migrateDb(domain, configurationName, db, path);
}
private static void postUpdateDatabase(final String domain, final String configurationName, final String db)
throws Exception {
final String path = getSQLScriptPath(db, "post");
migrateDb(domain, configurationName, db, path);
}
private static void cleanDatabase(final String domain, final String configurationName, final String db)
throws Exception {
final String path = getSQLScriptPath(db, "clean");
cleanDb(domain, configurationName, db, path);
}
private static String getSQLScriptPath(final String db, final String position) {
final StringBuilder migrationScript = new StringBuilder("/migration/");
migrationScript.append(db).append("-").append(position).append("-5.8-5.9.sql");
return migrationScript.toString();
}
private static void cleanDb(final String domain, final String configurationName, final String database,
final String resourcePath) throws Exception {
final SessionFactory sessionFactory = DbTool.getSessionFactory(domain,
configurationName.replaceAll("-configuration", "-session-factory"));
try {
InputStream inputStream = null;
try {
inputStream = getScriptStream(resourcePath);
executeScriptStepByStep(sessionFactory, inputStream, database);
} finally {
if (inputStream != null) {
inputStream.close();
}
}
} finally {
sessionFactory.close();
}
}
public static void migrateDb(final String domain, final String configurationName, final String database,
final String resourcePath) throws Exception {
final SessionFactory sessionFactory = DbTool.getSessionFactory(domain,
configurationName.replaceAll("-configuration", "-session-factory"));
try {
InputStream inputStream = null;
try {
inputStream = getScriptStream(resourcePath);
executeScript(sessionFactory, inputStream, database);
} finally {
if (inputStream != null) {
inputStream.close();
}
}
} finally {
sessionFactory.close();
}
}
private static InputStream getScriptStream(final String resourcePath) {
final InputStream inputStream = DbMigration.class.getResourceAsStream(resourcePath);
if (inputStream == null) {
final String message = ExceptionManager.getInstance().getFullMessage("bh_DBM_2");
throw new IllegalArgumentException(message);
}
return inputStream;
}
public static void executeScript(final SessionFactory sessionFactory, final InputStream inputStream, final String db) {
final byte[] bytes = IoUtil.readBytes(inputStream);
final String scriptContent = new String(bytes);
final List commands = getCommands(scriptContent, db);
final Session session = sessionFactory.openSession();
session.getTransaction().begin();
LOG.info("DB Commands Execution: " + commands.size());
for (final String command : commands) {
LOG.info("Executing command : " + command);
try {
session.createSQLQuery(command).executeUpdate();
} catch (final Exception e) {
LOG.error("Error while executing command: " + command, e);
}
}
session.getTransaction().commit();
session.close();
}
private static void executeScriptStepByStep(final SessionFactory sessionFactory, final InputStream inputStream,
final String db) {
final byte[] bytes = IoUtil.readBytes(inputStream);
final String scriptContent = new String(bytes);
final List commands = getCommands(scriptContent, db);
final Session session = sessionFactory.openSession();
LOG.info("DB Commands Execution: " + commands.size());
for (final String command : commands) {
LOG.info("Executing command : " + command);
try {
session.getTransaction().begin();
session.createSQLQuery(command).executeUpdate();
session.getTransaction().commit();
} catch (final Exception e) {
session.getTransaction().rollback();
LOG.warn("Error while executing command: " + command, e);
}
}
session.close();
}
public static List getCommands(final String scriptContent, final String db) {
String delimiter = ";";
if ("sqlserver".equals(db) || "sybase".equals(db)) {
delimiter = "go";
}
final String regex = delimiter.concat("\r?\n");
final List commands = new ArrayList();
final String[] tmp = scriptContent.split(regex);
for (final String command : tmp) {
if (command.trim().length() > 0) {
commands.add(command.trim());
}
}
final int lastIndex = commands.size() - 1;
if (lastIndex >= 0) {
String lastCommand = commands.get(lastIndex);
final int index = lastCommand.lastIndexOf(delimiter);
if (index > 0) {
lastCommand = lastCommand.substring(0, index);
commands.remove(lastIndex);
commands.add(lastCommand);
}
}
return commands;
}
private static void migrateTimerEvents(final String domain, final String configurationName) throws Exception {
final MigrateTimers timers = new MigrateTimers();
timers.execute(domain, configurationName);
}
private static void migrateSignalEvents(final String domain, final String configurationName) throws Exception {
final MigrateSignals signals = new MigrateSignals();
signals.execute(domain, configurationName);
}
private static void migrateErrorEvents(final String domain, final String configurationName) throws Exception {
final MigrateErrors errors = new MigrateErrors();
errors.execute(domain, configurationName);
}
private static void migrateDeadlineEvents(final String domain, final String configurationName) throws Exception {
final MigrateDeadlines deadlines = new MigrateDeadlines();
deadlines.execute(domain, configurationName);
}
private static void migrateAsyncEvents(final String domain, final String configurationName) throws Exception {
final MigrateAsyncs asyncs = new MigrateAsyncs();
asyncs.execute(domain, configurationName);
}
private static abstract class ExecuteInASession {
public abstract String getEventQuery();
public abstract void executeEventCouple(Session session, EventCouple couple);
protected int getIncomingRetries(final Session session, final IncomingEventInstance event) {
final String queryString = "select RETRIES_ from BN_IEI_ where DBID_ = :id";
final Query query = session.createSQLQuery(queryString);
query.setLong("id", event.getId());
final Object result = query.uniqueResult();
if (result instanceof BigDecimal) {
return ((BigDecimal) result).intValue();
} else {
return (Integer) result;
}
}
protected String getRootProcessInstanceUUID(final Session session, final ProcessInstanceUUID instanceUUID) {
final StringBuilder builder = new StringBuilder();
builder.append("SELECT process.rootInstanceUUID.value ");
builder.append("FROM org.ow2.bonita.facade.runtime.impl.InternalProcessInstance AS process ");
builder.append("WHERE process.instanceUUID.value = :processUUID");
final Query query = session.createQuery(builder.toString());
query.setString("processUUID", instanceUUID.getValue());
return (String) query.uniqueResult();
}
@SuppressWarnings("unchecked")
private static List getEvents(final Session session, final String queryString) {
final Query query = session.createQuery(queryString);
query.setResultTransformer(Transformers.aliasToBean(EventCouple.class));
query.setMaxResults(100);
final List couples = query.list();
if (couples == null) {
return Collections.emptyList();
}
return couples;
}
public void execute(final String domain, final String configurationName) throws Exception {
final SessionFactory sessionFactory = DbTool.getSessionFactory(domain,
configurationName.replaceAll("-configuration", "-session-factory"));
final Session session = sessionFactory.openSession();
try {
session.getTransaction().begin();
List eventCouples = null;
do {
eventCouples = getEvents(session, getEventQuery());
final int size = eventCouples.size();
int i = 1;
LOG.info("Getting " + size + " eventCouple(s)");
for (final EventCouple eventCouple : eventCouples) {
LOG.info("Migrating eventCouple: " + i + "/" + size);
executeEventCouple(session, eventCouple);
session.getTransaction().commit();
session.getTransaction().begin();
i++;
}
} while (!eventCouples.isEmpty());
} finally {
session.close();
}
}
}
private static class MigrateTimers extends ExecuteInASession {
@Override
public String getEventQuery() {
final StringBuilder builder = new StringBuilder();
builder.append("SELECT incoming AS incoming, outgoing AS outgoing ");
builder.append("FROM org.ow2.bonita.runtime.event.IncomingEventInstance AS incoming,");
builder.append(" org.ow2.bonita.runtime.event.OutgoingEventInstance AS outgoing ");
builder.append("WHERE incoming.name = outgoing.name ");
builder
.append("AND (incoming.signal = 'end_of_timer' OR incoming.signal = 'event.boundary.timer' OR incoming.signal = 'event.start.timer') ");
builder.append("AND incoming.locked = outgoing.locked ");
builder.append("AND (outgoing.processName IS NULL OR outgoing.processName = incoming.processName) ");
builder.append("AND (outgoing.activityName IS NULL OR outgoing.activityName = incoming.activityName) ");
builder.append("ORDER BY outgoing.id ASC, incoming.id ASC");
return builder.toString();
}
@Override
public void executeEventCouple(final Session session, final EventCouple couple) {
final IncomingEventInstance incoming = couple.getIncoming();
LOG.info(incoming.toString());
Job timer = null;
if ("event.start.timer".equals(incoming.getSignal())) {
timer = JobBuilder.startTimerJob(incoming.getActivityName(), incoming.getActivityDefinitionUUID(),
incoming.getExpression(), incoming.getEnableTime());
timer.setEventSubProcessRootInstanceUUID(incoming.getEventSubProcessRootInstanceUUID());
} else if ("event.boundary.timer".equals(incoming.getSignal())) {
final String uuid = getRootProcessInstanceUUID(session, incoming.getInstanceUUID());
timer = JobBuilder.boundaryTimerJob(incoming.getName(), new ProcessInstanceUUID(uuid),
incoming.getExecutionUUID(), incoming.getEnableTime(), incoming.getInstanceUUID());
} else if ("end_of_timer".equals(incoming.getSignal())) {
final String uuid = getRootProcessInstanceUUID(session, incoming.getInstanceUUID());
timer = JobBuilder.intermediateTimerJob(incoming.getActivityName(), new ProcessInstanceUUID(uuid),
incoming.getExecutionUUID(), incoming.getEnableTime(), incoming.getInstanceUUID());
}
timer.setRetries(getIncomingRetries(session, incoming));
session.delete(incoming);
LOG.info(timer.toString());
session.save(timer);
session.delete(couple.getOutgoing());
}
}
private static class MigrateSignals extends ExecuteInASession {
@Override
public String getEventQuery() {
final StringBuilder builder = new StringBuilder();
builder.append("SELECT incoming AS incoming, outgoing AS outgoing ");
builder.append("FROM org.ow2.bonita.runtime.event.IncomingEventInstance AS incoming,");
builder.append(" org.ow2.bonita.runtime.event.OutgoingEventInstance AS outgoing ");
builder.append("WHERE incoming.name = outgoing.name ");
builder
.append("AND (incoming.signal = 'event.intermediate.signal' OR incoming.signal = 'event.boundary.signal' OR incoming.signal = 'event.start.signal') ");
builder.append("AND incoming.locked = outgoing.locked ");
builder.append("AND (outgoing.processName IS NULL OR outgoing.processName = incoming.processName) ");
builder.append("AND (outgoing.activityName IS NULL OR outgoing.activityName = incoming.activityName) ");
builder.append("ORDER BY outgoing.id ASC, incoming.id ASC");
return builder.toString();
}
@Override
public void executeEventCouple(final Session session, final EventCouple couple) {
final IncomingEventInstance incoming = couple.getIncoming();
Job signal = null;
if (EventConstants.SIGNAL_START_EVENT.equals(incoming.getSignal())) {
signal = JobBuilder.startSignalJob(incoming.getName(), incoming.getActivityDefinitionUUID());
signal.setEventSubProcessRootInstanceUUID(incoming.getEventSubProcessRootInstanceUUID());
} else if (EventConstants.SIGNAL_BOUNDARY_EVENT.equals(incoming.getSignal())) {
final String uuid = getRootProcessInstanceUUID(session, incoming.getInstanceUUID());
signal = JobBuilder.boundarySignalJob(incoming.getActivityName(), new ProcessInstanceUUID(uuid),
incoming.getExecutionUUID(), incoming.getInstanceUUID());
session.delete(incoming);
} else if (EventConstants.SIGNAL_INTERMEDIATE_EVENT.equals(incoming.getSignal())) {
final String uuid = getRootProcessInstanceUUID(session, incoming.getInstanceUUID());
signal = JobBuilder.intermediateSignalJob(incoming.getName(), new ProcessInstanceUUID(uuid),
incoming.getExecutionUUID(), incoming.getInstanceUUID());
session.delete(incoming);
}
signal.setRetries(getIncomingRetries(session, incoming));
session.save(signal);
session.delete(couple.getOutgoing());
}
}
private static class MigrateErrors extends ExecuteInASession {
@Override
public String getEventQuery() {
final StringBuilder builder = new StringBuilder();
builder.append("SELECT incoming AS incoming, outgoing AS outgoing ");
builder.append("FROM org.ow2.bonita.runtime.event.IncomingEventInstance AS incoming,");
builder.append(" org.ow2.bonita.runtime.event.OutgoingEventInstance AS outgoing ");
builder.append("WHERE incoming.name = outgoing.name ");
builder.append("AND (incoming.signal = 'event.boundary.error' OR incoming.signal = 'event.start.error') ");
builder.append("AND incoming.locked = outgoing.locked ");
builder.append("AND (outgoing.processName IS NULL OR outgoing.processName = incoming.processName) ");
builder.append("AND (outgoing.activityName IS NULL OR outgoing.activityName = incoming.activityName) ");
builder.append("ORDER BY outgoing.id ASC, incoming.id ASC");
return builder.toString();
}
@Override
public void executeEventCouple(final Session session, final EventCouple couple) {
final IncomingEventInstance incoming = couple.getIncoming();
Job error = null;
if ("event.start.error".equals(incoming.getSignal())) {
error = JobBuilder.startErrorJob(incoming.getName(), incoming.getActivityDefinitionUUID());
error.setEventSubProcessRootInstanceUUID(incoming.getEventSubProcessRootInstanceUUID());
} else if ("event.boundary.error".equals(incoming.getSignal())) {
final String uuid = getRootProcessInstanceUUID(session, incoming.getInstanceUUID());
final String incomingEventName = incoming.getName();
final int separator = incomingEventName.indexOf(EventConstants.SEPARATOR);
final String eventName = incomingEventName.substring(0, separator);
error = JobBuilder.boundaryErrorJob(eventName, new ProcessInstanceUUID(uuid), incoming.getExecutionUUID(),
incoming.getInstanceUUID());
session.delete(incoming);
}
error.setRetries(getIncomingRetries(session, incoming));
LOG.info(error.toString());
session.save(error);
session.delete(couple.getOutgoing());
}
}
private static class MigrateDeadlines extends ExecuteInASession {
@Override
public String getEventQuery() {
final StringBuilder builder = new StringBuilder();
builder.append("SELECT incoming AS incoming, outgoing AS outgoing ");
builder.append("FROM org.ow2.bonita.runtime.event.IncomingEventInstance AS incoming,");
builder.append(" org.ow2.bonita.runtime.event.OutgoingEventInstance AS outgoing ");
builder.append("WHERE incoming.name = outgoing.name ");
builder.append("AND incoming.signal = 'timer' ");
builder.append("AND incoming.locked = outgoing.locked ");
builder.append("AND (outgoing.processName IS NULL OR outgoing.processName = incoming.processName) ");
builder.append("AND (outgoing.activityName IS NULL OR outgoing.activityName = incoming.activityName) ");
builder.append("ORDER BY outgoing.id ASC, incoming.id ASC");
return builder.toString();
}
@Override
public void executeEventCouple(final Session session, final EventCouple couple) {
final IncomingEventInstance incoming = couple.getIncoming();
final OutgoingEventInstance outgoing = couple.getOutgoing();
final ProcessInstanceUUID instanceUUID = incoming.getInstanceUUID();
final String uuid = getRootProcessInstanceUUID(session, instanceUUID);
final String deadlineId = String.valueOf(outgoing.getParameters().get("id"));
final Job deadline = JobBuilder.deadlineJob(deadlineId, new ProcessInstanceUUID(uuid),
incoming.getExecutionUUID(), incoming.getEnableTime(), incoming.getInstanceUUID());
deadline.setRetries(getIncomingRetries(session, incoming));
session.save(deadline);
session.delete(outgoing);
session.delete(incoming);
}
}
private static class MigrateAsyncs extends ExecuteInASession {
@Override
public String getEventQuery() {
final StringBuilder builder = new StringBuilder();
builder.append("SELECT incoming AS incoming, outgoing AS outgoing ");
builder.append("FROM org.ow2.bonita.runtime.event.IncomingEventInstance AS incoming,");
builder.append(" org.ow2.bonita.runtime.event.OutgoingEventInstance AS outgoing ");
builder.append("WHERE incoming.name = outgoing.name ");
builder.append("AND incoming.signal = 'async' ");
builder.append("AND incoming.locked = outgoing.locked ");
builder.append("AND (outgoing.processName IS NULL OR outgoing.processName = incoming.processName) ");
builder.append("AND (outgoing.activityName IS NULL OR outgoing.activityName = incoming.activityName) ");
builder.append("ORDER BY outgoing.id ASC, incoming.id ASC");
return builder.toString();
}
@Override
public void executeEventCouple(final Session session, final EventCouple couple) {
final IncomingEventInstance incoming = couple.getIncoming();
final OutgoingEventInstance outgoing = couple.getOutgoing();
final ProcessInstanceUUID instanceUUID = incoming.getInstanceUUID();
final String uuid = getRootProcessInstanceUUID(session, instanceUUID);
final Job async = JobBuilder.asyncJob(incoming.getName(), new ProcessInstanceUUID(uuid),
incoming.getExecutionUUID(), instanceUUID);
async.setEventSubProcessRootInstanceUUID(incoming.getEventSubProcessRootInstanceUUID());
async.setRetries(getIncomingRetries(session, incoming));
session.save(async);
session.delete(outgoing);
session.delete(incoming);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy