Package org.apache.cayenne.unit.di.server

Source Code of org.apache.cayenne.unit.di.server.SchemaBuilder

/*****************************************************************
*   Licensed to the Apache Software Foundation (ASF) under one
*  or more contributor license agreements.  See the NOTICE file
*  distributed with this work for additional information
*  regarding copyright ownership.  The ASF licenses this file
*  to you 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 org.apache.cayenne.unit.di.server;

import java.io.InputStream;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.ListIterator;

import org.apache.cayenne.access.DataDomain;
import org.apache.cayenne.access.DataNode;
import org.apache.cayenne.access.DbGenerator;
import org.apache.cayenne.access.dbsync.SkipSchemaUpdateStrategy;
import org.apache.cayenne.ashwood.AshwoodEntitySorter;
import org.apache.cayenne.cache.MapQueryCache;
import org.apache.cayenne.dba.DbAdapter;
import org.apache.cayenne.di.Inject;
import org.apache.cayenne.event.DefaultEventManager;
import org.apache.cayenne.log.JdbcEventLogger;
import org.apache.cayenne.map.DataMap;
import org.apache.cayenne.map.DbAttribute;
import org.apache.cayenne.map.DbEntity;
import org.apache.cayenne.map.MapLoader;
import org.apache.cayenne.map.Procedure;
import org.apache.cayenne.testdo.testmap.StringET1ExtendedType;
import org.apache.cayenne.unit.UnitDbAdapter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xml.sax.InputSource;

/**
* Default implementation of the AccessStack that has a single DataNode per DataMap.
*/
public class SchemaBuilder {

    private static Log logger = LogFactory.getLog(SchemaBuilder.class);

    public static final String CONNECTION_NAME_KEY = "cayenneTestConnection";
    public static final String DEFAULT_CONNECTION_KEY = "internal_embedded_datasource";

    public static final String SKIP_SCHEMA_KEY = "cayenne.test.schema.skip";

    private static String[] DATA_MAPS_REQUIREING_SCHEMA_SETUP = {
            "testmap.map.xml", "people.map.xml", "locking.map.xml",
            "relationships.map.xml", "multi-tier.map.xml", "generic.map.xml",
            "map-db1.map.xml", "map-db2.map.xml", "embeddable.map.xml",
            "qualified.map.xml", "quoted-identifiers.map.xml",
            "inheritance-single-table1.map.xml", "inheritance-vertical.map.xml"
    };

    // hardcoded dependent entities that should be excluded
    // if LOBs are not supported
    private static final String[] EXTRA_EXCLUDED_FOR_NO_LOB = new String[] {
        "CLOB_DETAIL"
    };

    private ServerCaseDataSourceFactory dataSourceFactory;
    private UnitDbAdapter unitDbAdapter;
    private DbAdapter dbAdapter;
    private DataDomain domain;
    private JdbcEventLogger jdbcEventLogger;

    public SchemaBuilder(@Inject ServerCaseDataSourceFactory dataSourceFactory,
            @Inject UnitDbAdapter unitDbAdapter, @Inject DbAdapter dbAdapter,
            @Inject JdbcEventLogger jdbcEventLogger) {
        this.dataSourceFactory = dataSourceFactory;
        this.unitDbAdapter = unitDbAdapter;
        this.dbAdapter = dbAdapter;
        this.jdbcEventLogger = jdbcEventLogger;
    }

    /**
     * Completely rebuilds test schema.
     */
    // TODO - this method changes the internal state of the object ... refactor
    public void rebuildSchema() {

        if ("true".equalsIgnoreCase(System.getProperty(SKIP_SCHEMA_KEY))) {
            logger.info("skipping schema generation... ");
            return;
        }

        // generate schema combining all DataMaps that require schema support.
        // Schema generation is done like that instead of per DataMap on demand to avoid
        // conflicts when dropping and generating PK objects.

        DataMap[] maps = new DataMap[DATA_MAPS_REQUIREING_SCHEMA_SETUP.length];

        for (int i = 0; i < maps.length; i++) {
            InputStream stream = getClass().getClassLoader().getResourceAsStream(
                    DATA_MAPS_REQUIREING_SCHEMA_SETUP[i]);
            InputSource in = new InputSource(stream);
            in.setSystemId(DATA_MAPS_REQUIREING_SCHEMA_SETUP[i]);
            maps[i] = new MapLoader().loadDataMap(in);
        }

        this.domain = new DataDomain("temp");
        domain.setEventManager(new DefaultEventManager(2));
        domain.setEntitySorter(new AshwoodEntitySorter());
        domain.setQueryCache(new MapQueryCache(50));

        try {
            for (DataMap map : maps) {
                initNode(map);
            }

            dropSchema();
            dropPKSupport();
            createSchema();
            createPKSupport();
        }
        catch (Exception e) {
            throw new RuntimeException("Error rebuilding schema", e);
        }
    }

    private void initNode(DataMap map) throws Exception {

        DataNode node = new DataNode(map.getName());
        node.setJdbcEventLogger(jdbcEventLogger);
        node.setAdapter(dbAdapter);
        node.setDataSource(dataSourceFactory.getSharedDataSource());

        // setup test extended types
        node.getAdapter().getExtendedTypes().registerType(new StringET1ExtendedType());

        // tweak mapping with a delegate
        for (Procedure proc : map.getProcedures()) {
            unitDbAdapter.tweakProcedure(proc);
        }

        node.addDataMap(map);

        node.setSchemaUpdateStrategy(new SkipSchemaUpdateStrategy());
        domain.addNode(node);
    }

    /** Drops all test tables. */
    private void dropSchema() throws Exception {
        for (DataNode node : domain.getDataNodes()) {
            dropSchema(node, node.getDataMaps().iterator().next());
        }
    }

    /**
     * Creates all test tables in the database.
     */
    private void createSchema() throws Exception {
        for (DataNode node : domain.getDataNodes()) {
            createSchema(node, node.getDataMaps().iterator().next());
        }
    }

    public void dropPKSupport() throws Exception {
        for (DataNode node : domain.getDataNodes()) {
            dropPKSupport(node, node.getDataMaps().iterator().next());
        }
    }

    /**
     * Creates primary key support for all node DbEntities. Will use its facilities
     * provided by DbAdapter to generate any necessary database objects and data for
     * primary key support.
     */
    public void createPKSupport() throws Exception {
        for (DataNode node : domain.getDataNodes()) {
            createPKSupport(node, node.getDataMaps().iterator().next());
        }
    }

    /**
     * Helper method that orders DbEntities to satisfy referential constraints and returns
     * an ordered list.
     */
    private List<DbEntity> dbEntitiesInInsertOrder(DataNode node, DataMap map) {
        List<DbEntity> entities = new ArrayList<DbEntity>(map.getDbEntities());

        // filter various unsupported tests...

        // LOBs
        boolean excludeLOB = !unitDbAdapter.supportsLobs();
        boolean excludeBinPK = !unitDbAdapter.supportsBinaryPK();
        if (excludeLOB || excludeBinPK) {

            List<DbEntity> filtered = new ArrayList<DbEntity>();

            for (DbEntity ent : entities) {

                // check for LOB attributes
                if (excludeLOB) {
                    if (Arrays.binarySearch(EXTRA_EXCLUDED_FOR_NO_LOB, ent.getName()) >= 0) {
                        continue;
                    }

                    boolean hasLob = false;
                    for (final DbAttribute attr : ent.getAttributes()) {
                        if (attr.getType() == Types.BLOB || attr.getType() == Types.CLOB) {
                            hasLob = true;
                            break;
                        }
                    }

                    if (hasLob) {
                        continue;
                    }
                }

                // check for BIN PK
                if (excludeBinPK) {
                    boolean skip = false;
                    for (final DbAttribute attr : ent.getAttributes()) {
                        // check for BIN PK or FK to BIN Pk
                        if (attr.getType() == Types.BINARY
                                || attr.getType() == Types.VARBINARY
                                || attr.getType() == Types.LONGVARBINARY) {

                            if (attr.isPrimaryKey() || attr.isForeignKey()) {
                                skip = true;
                                break;
                            }
                        }
                    }

                    if (skip) {
                        continue;
                    }
                }

                filtered.add(ent);
            }

            entities = filtered;
        }

        domain.getEntitySorter().sortDbEntities(entities, false);
        return entities;
    }

    private void dropSchema(DataNode node, DataMap map) throws Exception {
        Connection conn = dataSourceFactory.getSharedDataSource().getConnection();
        List<DbEntity> list = dbEntitiesInInsertOrder(node, map);

        try {
            DatabaseMetaData md = conn.getMetaData();
            ResultSet tables = md.getTables(null, null, "%", null);
            List<String> allTables = new ArrayList<String>();

            while (tables.next()) {
                // 'toUpperCase' is needed since most databases
                // are case insensitive, and some will convert names to lower case
                // (PostgreSQL)
                String name = tables.getString("TABLE_NAME");
                if (name != null)
                    allTables.add(name.toUpperCase());
            }
            tables.close();

            unitDbAdapter.willDropTables(conn, map, allTables);

            // drop all tables in the map
            Statement stmt = conn.createStatement();

            ListIterator<DbEntity> it = list.listIterator(list.size());
            while (it.hasPrevious()) {
                DbEntity ent = it.previous();
                if (!allTables.contains(ent.getName().toUpperCase())) {
                    continue;
                }

                for (String dropSql : node.getAdapter().dropTableStatements(ent)) {
                    try {
                        logger.info(dropSql);
                        stmt.execute(dropSql);
                    }
                    catch (SQLException sqe) {
                        logger.warn(
                                "Can't drop table " + ent.getName() + ", ignoring...",
                                sqe);
                    }
                }
            }

            unitDbAdapter.droppedTables(conn, map);
        }
        finally {
            conn.close();
        }

    }

    private void dropPKSupport(DataNode node, DataMap map) throws Exception {
        List<DbEntity> filteredEntities = dbEntitiesInInsertOrder(node, map);
        node.getAdapter().getPkGenerator().dropAutoPk(node, filteredEntities);
    }

    private void createPKSupport(DataNode node, DataMap map) throws Exception {
        List<DbEntity> filteredEntities = dbEntitiesInInsertOrder(node, map);
        node.getAdapter().getPkGenerator().createAutoPk(node, filteredEntities);
    }

    private void createSchema(DataNode node, DataMap map) throws Exception {
        Connection conn = dataSourceFactory.getSharedDataSource().getConnection();

        try {
            unitDbAdapter.willCreateTables(conn, map);
            Statement stmt = conn.createStatement();

            for (String query : tableCreateQueries(node, map)) {
                logger.info(query);
                stmt.execute(query);
            }
            unitDbAdapter.createdTables(conn, map);
        }
        finally {
            conn.close();
        }
    }

    /**
     * Returns iterator of preprocessed table create queries.
     */
    private Collection<String> tableCreateQueries(DataNode node, DataMap map)
            throws Exception {
        DbAdapter adapter = node.getAdapter();
        DbGenerator gen = new DbGenerator(adapter, map, null, domain, jdbcEventLogger);

        List<DbEntity> orderedEnts = dbEntitiesInInsertOrder(node, map);
        List<String> queries = new ArrayList<String>();

        // table definitions
        for (DbEntity ent : orderedEnts) {
            queries.add(adapter.createTable(ent));
        }

        // FK constraints
        for (DbEntity ent : orderedEnts) {
            if (!unitDbAdapter.supportsFKConstraints(ent)) {
                continue;
            }

            List<String> qs = gen.createConstraintsQueries(ent);
            queries.addAll(qs);
        }

        return queries;
    }
}
TOP

Related Classes of org.apache.cayenne.unit.di.server.SchemaBuilder

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.