* Copyright 2012-2017 the original author or authors.
 * 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,
 * See the License for the specific language governing permissions and
 * limitations under the License.
package org.teiid.spring.autoconfigure;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import javax.sql.DataSource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.boot.Metadata;
import org.hibernate.dialect.Dialect;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.spi.ServiceRegistryImplementor;
import org.hibernate.tool.schema.SourceType;
import org.hibernate.tool.schema.internal.ExceptionHandlerHaltImpl;
import org.hibernate.tool.schema.internal.HibernateSchemaManagementTool;
import org.hibernate.tool.schema.internal.SchemaCreatorImpl;
import org.hibernate.tool.schema.internal.SchemaDropperImpl;
import org.hibernate.tool.schema.internal.exec.GenerationTarget;
import org.hibernate.tool.schema.spi.ExceptionHandler;
import org.hibernate.tool.schema.spi.ExecutionOptions;
import org.hibernate.tool.schema.spi.ScriptSourceInput;
import org.hibernate.tool.schema.spi.SourceDescriptor;
import org.springframework.context.ApplicationContext;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.teiid.core.types.JDBCSQLTypeInfo;
import org.teiid.metadata.Schema;
import org.teiid.translator.TypeFacility;

class RedirectionSchemaInitializer extends MultiDataSourceInitializer {
    private static final Log logger = LogFactory.getLog(RedirectionSchemaInitializer.class);

    private Dialect dialect;
    private Schema schema;
    private Metadata metadata;
    private ServiceRegistry registry;

    RedirectionSchemaInitializer(DataSource dataSource, String sourceName, Dialect dialect, Metadata metadata,
            ServiceRegistry registry, Schema schema, ApplicationContext applicationContext) {
        super(dataSource, sourceName, applicationContext);
        this.dialect = dialect;
        this.metadata = metadata;
        this.schema = schema;
        this.registry = registry;

    List<Resource> getScripts(String propertyName, List<String> resources, String fallback) {
        List<Resource> found = super.getScripts(propertyName, resources, fallback);
        String key = "spring.datasource." + sourceName + ".schema";
        if (key.equals(propertyName)) {
            // if scripts are found do not run them again, as they would have executed first
            // time data source
            // is registered.
            if (!found.isEmpty()) {
                return Collections.emptyList();
            found = generatedScripts();
        return found;

    List<Resource> generatedScripts() {
        List<Resource> resources = Collections.emptyList();

        for (PersistentClass clazz : metadata.getEntityBindings()) {
            org.hibernate.mapping.Table ormTable = clazz.getTable();
            String tableName = ormTable.getQuotedName();
            if (this.schema.getTable(tableName) != null) {
                org.hibernate.mapping.Column c = new org.hibernate.mapping.Column(
                ormTable.setName(tableName + TeiidConstants.REDIRECTED_TABLE_POSTFIX);

        List<String> statements = createScript(metadata, dialect, true);
        StringBuilder sb = new StringBuilder();
        for (String s : statements) {
            // we have no need for sequences in the redirected scenario, they are fed from
            // other side.
            if (s.startsWith("drop sequence") || s.startsWith("create sequence")) {
        logger.debug("Redirected Schema:\n" + sb.toString());
        resources = Arrays.asList(new ByteArrayResource(sb.toString().getBytes()));
        return resources;

    List<String> createScript(Metadata metadata, Dialect d, boolean includeDrops) {
        final JournalingGenerationTarget target = new JournalingGenerationTarget();

        final ExecutionOptions options = new ExecutionOptions() {
            public boolean shouldManageNamespaces() {
                return false;

            public Map getConfigurationValues() {
                return Collections.emptyMap();

            public ExceptionHandler getExceptionHandler() {
                return ExceptionHandlerHaltImpl.INSTANCE;
        HibernateSchemaManagementTool tool = new HibernateSchemaManagementTool();
        tool.injectServices((ServiceRegistryImplementor) this.registry);
        SourceDescriptor sd = new SourceDescriptor() {
            public SourceType getSourceType() {
                return SourceType.METADATA;

            public ScriptSourceInput getScriptSourceInput() {
                return null;
        if (includeDrops) {
            new SchemaDropperImpl(tool).doDrop(metadata, options, d, sd, target);
        new SchemaCreatorImpl(tool).doCreation(metadata, d, options, sd, target);
        return target.commands;

    private static class JournalingGenerationTarget implements GenerationTarget {
        private final ArrayList<String> commands = new ArrayList<String>();

        public void prepare() {

        public void accept(String command) {

        public void release() {