001/**
002 * Copyright 2005-2017 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.kuali.rice.sql.spring;
017
018import java.util.Collections;
019import java.util.List;
020
021import javax.sql.DataSource;
022
023import com.google.common.base.Optional;
024import com.google.common.collect.Lists;
025import org.kuali.common.jdbc.model.context.JdbcContext;
026import org.kuali.common.jdbc.reset.DefaultJdbcResetConfig;
027import org.kuali.common.jdbc.service.spring.DataSourceConfig;
028import org.kuali.common.jdbc.sql.spring.DbaContextConfig;
029import org.kuali.common.jdbc.sql.spring.JdbcContextsConfig;
030import org.kuali.common.jdbc.suppliers.ResourcesSupplierFactory;
031import org.kuali.common.jdbc.suppliers.SqlSupplier;
032import org.kuali.common.jdbc.suppliers.spring.SuppliersFactoryConfig;
033import org.kuali.common.jdbc.vendor.model.DatabaseVendor;
034import org.kuali.common.util.metainf.model.PathComparator;
035import org.kuali.common.util.metainf.service.MetaInfUtils;
036import org.kuali.common.util.metainf.spring.MetaInfDataLocation;
037import org.kuali.common.util.metainf.spring.MetaInfDataType;
038import org.kuali.common.util.metainf.spring.MetaInfGroup;
039import org.kuali.rice.db.config.profile.MetaInfDataLocationProfileConfig;
040import org.kuali.rice.db.config.profile.MetaInfDataTypeProfileConfig;
041import org.kuali.rice.db.config.profile.MetaInfFilterConfig;
042import org.kuali.rice.db.config.profile.RiceClientBootstrapConfig;
043import org.kuali.rice.db.config.profile.RiceClientDemoConfig;
044import org.kuali.rice.db.config.profile.RiceServerDemoConfig;
045import org.kuali.rice.db.config.profile.RiceServerDemoFilterConfig;
046import org.kuali.rice.db.config.profile.RiceMasterConfig;
047import org.kuali.rice.db.config.profile.RiceServerBootstrapConfig;
048import org.kuali.rice.sql.project.SqlProjectConstants;
049import org.springframework.beans.factory.annotation.Autowired;
050import org.springframework.context.annotation.Bean;
051import org.springframework.context.annotation.Configuration;
052import org.springframework.context.annotation.Import;
053
054import com.google.common.collect.ImmutableList;
055
056/**
057 * Used by developers (to reset their local db), CI (to validate changes), and by the deploy process to reset the
058 * database for instances of the running application.
059 *
060 * @author Kuali Rice Team (rice.collab@kuali.org)
061 */
062@Configuration
063@Import({ DbaContextConfig.class, SuppliersFactoryConfig.class, DefaultJdbcResetConfig.class,
064          RiceClientBootstrapConfig.class, RiceClientDemoConfig.class, RiceServerBootstrapConfig.class, RiceServerDemoConfig.class,
065          RiceServerDemoFilterConfig.class, RiceMasterConfig.class })
066public class SourceSqlConfig implements JdbcContextsConfig {
067
068    // All paths must have the hardcoded separator to be consistent for deployment
069    private static final String PATH_SEPARATOR = "/";
070
071    private static final String INITIAL_SQL_PATH = "initial-sql" + PATH_SEPARATOR + "2.3.0";
072    private static final String UPGRADE_SQL_PATH = "upgrades" + PATH_SEPARATOR + "*";
073
074    /**
075     * The DBA context.
076     */
077        @Autowired
078        DbaContextConfig dba;
079
080    /**
081     * The factory for creating SQL resources.
082     */
083        @Autowired
084        ResourcesSupplierFactory factory;
085
086    /**
087     * The vendor of the database to run the SQL against.
088     */
089        @Autowired
090        DatabaseVendor vendor;
091
092    /**
093     * The data source configuration.
094     */
095        @Autowired
096        DataSourceConfig dataSources;
097
098    /**
099     * The {@link MetaInfDataLocation} profile.
100     */
101    @Autowired(required = false)
102    MetaInfDataLocationProfileConfig locationConfig;
103
104    /**
105     * The {@link MetaInfDataType} profile.
106     */
107    @Autowired(required = false)
108    MetaInfDataTypeProfileConfig typeConfig;
109
110    /**
111     * The data filtering profile.
112     */
113    @Autowired(required = false)
114    MetaInfFilterConfig serverDemoFilterConfig;
115
116    /**
117     * {@inheritDoc}
118     *
119     * <p>
120     * All of the initial data (the data included in the {@code MetaInfGroup.SCHEMA}, {@code MetaInfGroup.CONSTRAINTS},
121     * or {@code MetaInfGroup.DATA} groups) needs to be applied before the update data (the data included in the
122     * {@code MetaInfGroup.OTHER} group).
123     * </p>
124     */
125        @Override
126        @Bean
127        public List<JdbcContext> jdbcContexts() {
128        List<JdbcContext> jdbcContexts = Lists.newArrayList();
129
130        List<MetaInfDataType> types = getTypes();
131        List<MetaInfDataLocation> locations = getLocations();
132        List<MetaInfGroup> groups = Lists.newArrayList(MetaInfGroup.SCHEMA, MetaInfGroup.DATA, MetaInfGroup.CONSTRAINTS);
133
134        jdbcContexts.add(dba.dbaBeforeContext());
135
136        for (MetaInfDataType type : types) {
137            for (MetaInfDataLocation location : locations) {
138                for (MetaInfGroup group : groups) {
139                    jdbcContexts.add(getJdbcContext(group, INITIAL_SQL_PATH, vendor.getCode(), location, type, true));
140                }
141
142                jdbcContexts.add(getJdbcContext(MetaInfGroup.OTHER, UPGRADE_SQL_PATH, vendor.getCode(), location, type, false));
143            }
144        }
145
146        jdbcContexts.add(dba.dbaAfterContext());
147
148        return ImmutableList.copyOf(jdbcContexts);
149        }
150
151    /**
152     * Returns the list of {@link MetaInfDataType}s to be applied to the database, returning an empty list if no
153     * profiles are active.
154     *
155     * @return the list of {@link MetaInfDataType}s to be applied to the database (if any)
156     */
157    protected List<MetaInfDataType> getTypes() {
158        return typeConfig != null ? typeConfig.getMetaInfDataTypes() : Lists.<MetaInfDataType> newArrayList();
159    }
160
161    /**
162     * Returns the list of {@link MetaInfDataLocation}s to be applied to the database, returning an empty list if no
163     * profiles are active.
164     *
165     * @return the list of {@link MetaInfDataLocation}s to be applied to the database (if any)
166     */
167    protected List<MetaInfDataLocation> getLocations() {
168        return locationConfig != null ? locationConfig.getMetaInfDataLocations() : Lists.<MetaInfDataLocation> newArrayList();
169    }
170
171    /**
172     * Creates the JDBC context for the given {@code group}, {@code location}, and {@code type}.
173     *
174     * @param group the group of the data to create the context for
175     * @param qualifier the prefix to add to the initial resource path
176     * @param vendor the database vendor to create the context for
177     * @param location the location of the data to create the context for
178     * @param type the type of data to create the context for
179     * @param multithreaded whether or not to run the context in multiple threads
180     *
181     * @return the JDBC context
182     */
183        protected JdbcContext getJdbcContext(MetaInfGroup group, String qualifier, String vendor, MetaInfDataLocation location, MetaInfDataType type, boolean multithreaded) {
184        DataSource dataSource = dataSources.dataSource();
185
186        List<SqlSupplier> suppliers = Lists.newArrayList();
187
188        PathComparator comparator = new PathComparator();
189
190        if (isIncluded(group, location, type) && !isExcluded(group, location, type)) {
191            List<String> resources = MetaInfUtils.getPatternedClasspathResources(SqlProjectConstants.ID,
192                    Optional.of(qualifier + PATH_SEPARATOR + vendor), Optional.of(location), Optional.of(type), group.name().toLowerCase());
193            Collections.sort(resources, comparator);
194            suppliers.addAll(factory.getSuppliers(resources));
195        }
196
197        String message = "[" + group.name().toLowerCase() + ":" + (multithreaded ? "concurrent" : "sequential") + "]";
198
199        return new JdbcContext.Builder(dataSource, suppliers).message(message).multithreaded(multithreaded).build();
200        }
201
202    /**
203     * Returns whether to include the data for the {@link MetaInfGroup}, {@link MetaInfDataLocation}, and
204     * {@link MetaInfDataType}.
205     *
206     * @param group the {link MetaInfGroup} to check
207     * @param location the {@link MetaInfDataLocation} to check
208     * @param type the {@link MetaInfDataType} to check
209     *
210     * @return true if the data set should be included, false otherwise
211     */
212    protected boolean isIncluded(MetaInfGroup group, MetaInfDataLocation location, MetaInfDataType type) {
213        return serverDemoFilterConfig == null || serverDemoFilterConfig.isIncluded(group, location, type);
214    }
215
216    /**
217     * Returns whether to exclude the data for the {@link MetaInfGroup}, {@link MetaInfDataLocation}, and
218     * {@link MetaInfDataType}.
219     *
220     * @param group the {link MetaInfGroup} to check
221     * @param location the {@link MetaInfDataLocation} to check
222     * @param type the {@link MetaInfDataType} to check
223     *
224     * @return true if the data set should be excluded, false otherwise
225     */
226    protected boolean isExcluded(MetaInfGroup group, MetaInfDataLocation location, MetaInfDataType type) {
227        return serverDemoFilterConfig != null && serverDemoFilterConfig.isExcluded(group, location, type);
228    }
229
230}