com.haulmont.cuba.core.sys.dbupdate.DbUpdaterImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.haulmont.cuba.core.sys.dbupdate.DbUpdaterImpl.java

Source

/*
 * Copyright (c) 2008-2016 Haulmont.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
package com.haulmont.cuba.core.sys.dbupdate;

import com.google.common.base.Joiner;
import com.haulmont.cuba.core.Persistence;
import com.haulmont.cuba.core.app.ClusterManagerAPI;
import com.haulmont.cuba.core.app.ServerConfig;
import com.haulmont.cuba.core.global.Configuration;
import com.haulmont.cuba.core.global.Events;
import com.haulmont.cuba.core.global.ScriptExecutionPolicy;
import com.haulmont.cuba.core.global.Scripting;
import com.haulmont.cuba.core.sys.DbInitializationException;
import com.haulmont.cuba.core.sys.DbUpdater;
import com.haulmont.cuba.core.sys.PostUpdateScripts;
import com.haulmont.cuba.core.sys.events.AppContextInitializedEvent;
import com.haulmont.cuba.core.sys.persistence.DbmsType;
import groovy.lang.Binding;
import groovy.lang.Closure;
import org.apache.commons.lang.StringUtils;
import org.slf4j.LoggerFactory;
import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.inject.Inject;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Component(DbUpdater.NAME)
public class DbUpdaterImpl extends DbUpdaterEngine {

    @Inject
    protected Scripting scripting;

    @Inject
    protected Persistence persistence;

    @Inject
    protected ClusterManagerAPI clusterManager;

    @Inject
    protected ServerConfig serverConfig;

    protected PostUpdateScripts postUpdate;

    protected Map<Closure, ScriptResource> postUpdateScripts = new HashMap<>();

    @Inject
    public void setConfigProvider(Configuration configuration) {
        String dbDirName = configuration.getConfig(ServerConfig.class).getDbDir();
        if (dbDirName != null)
            this.dbScriptsDirectory = dbDirName;

        dbmsType = DbmsType.getType();
        dbmsVersion = DbmsType.getVersion();
    }

    @EventListener(AppContextInitializedEvent.class)
    @Order(Events.LOWEST_PLATFORM_PRECEDENCE - 90) // after starting cluster
    protected void applicationInitialized() {
        if (clusterManager.isMaster() && serverConfig.getAutomaticDatabaseUpdate()) {
            updateDatabaseOnStart();
        } else {
            checkDatabaseOnStart();
        }
    }

    protected void updateDatabaseOnStart() {
        try {
            updateDatabase();
        } catch (DbInitializationException e) {
            throw new RuntimeException(
                    "\n" + "==============================================================================\n"
                            + "ERROR: Cannot check and update database. See the stacktrace below for details.\n"
                            + "==============================================================================",
                    e);
        }
    }

    protected void checkDatabaseOnStart() {
        try {
            boolean initialized = dbInitialized();
            if (!initialized) {
                throw new IllegalStateException(
                        "\n" + "============================================================================\n"
                                + "ERROR: Database is not initialized. Set 'cuba.automaticDatabaseUpdate'\n"
                                + "application property to 'true' to initialize and update database on startup.\n"
                                + "============================================================================");
            }
            List<String> scripts = findUpdateDatabaseScripts();
            if (!scripts.isEmpty()) {
                log.warn("\n" + "====================================================================\n"
                        + "WARNING: The application contains unapplied database update scripts:\n\n"
                        + Joiner.on('\n').join(scripts) + "\n\n"
                        + "Set 'cuba.automaticDatabaseUpdate' application property to 'true' to\n "
                        + "initialize and update database on startup.\n"
                        + "====================================================================");
            }
        } catch (DbInitializationException e) {
            throw new RuntimeException(
                    "\n" + "===================================================================\n"
                            + "ERROR: Cannot check database. See the stacktrace below for details.\n"
                            + "===================================================================",
                    e);
        }

    }

    @Override
    public DataSource getDataSource() {
        return persistence.getDataSource();
    }

    @Override
    protected boolean executeGroovyScript(final ScriptResource file) {
        Binding bind = new Binding();
        bind.setProperty("ds", getDataSource());
        bind.setProperty("log", LoggerFactory.getLogger(String.format("%s$%s", DbUpdaterEngine.class.getName(),
                StringUtils.removeEndIgnoreCase(file.getName(), ".groovy"))));
        if (!StringUtils.endsWithIgnoreCase(file.getName(), "." + UPGRADE_GROOVY_EXTENSION)) {
            bind.setProperty("postUpdate", new PostUpdateScripts() {
                @Override
                public void add(Closure closure) {
                    postUpdateScripts.put(closure, file);

                    postUpdate.add(closure);
                }

                @Override
                public List<Closure> getUpdates() {
                    return postUpdate.getUpdates();
                }
            });
        }

        try {
            scripting.evaluateGroovy(file.getContent(), bind, ScriptExecutionPolicy.DO_NOT_USE_COMPILE_CACHE);
        } catch (Exception e) {
            throw new RuntimeException(ERROR + "Error executing Groovy script " + file.name + "\n" + e.getMessage(),
                    e);
        }
        return !postUpdateScripts.containsValue(file);
    }

    @Override
    protected void doUpdate() {
        postUpdate = new PostUpdateScripts();

        try {
            super.doUpdate();
        } catch (RuntimeException e) {
            postUpdateScripts.clear();
            throw e;
        }

        if (!postUpdate.getUpdates().isEmpty()) {
            log.info(String.format("Execute '%s' post update actions", postUpdate.getUpdates().size()));

            for (Closure closure : postUpdate.getUpdates()) {
                ScriptResource groovyFile = postUpdateScripts.remove(closure);
                if (groovyFile != null) {
                    log.info("Execute post update from " + getScriptName(groovyFile));
                }

                closure.call();

                if (groovyFile != null && !postUpdateScripts.containsValue(groovyFile)) {
                    log.info("All post update actions completed for " + getScriptName(groovyFile));

                    markScript(getScriptName(groovyFile), false);
                }
            }
        }
    }
}