001    /**
002     * Copyright 2010-2013 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     */
016    package org.kuali.common.jdbc.spring;
017    
018    import java.util.ArrayList;
019    import java.util.List;
020    
021    import javax.sql.DataSource;
022    
023    import org.apache.commons.lang3.StringUtils;
024    import org.kuali.common.jdbc.JdbcExecutable;
025    import org.kuali.common.jdbc.context.JdbcContext;
026    import org.kuali.common.jdbc.context.SqlExecutionContext;
027    import org.kuali.common.jdbc.context.SqlMode;
028    import org.kuali.common.jdbc.listener.LogSqlListener;
029    import org.kuali.common.jdbc.listener.NotifyingListener;
030    import org.kuali.common.jdbc.listener.ProgressListener;
031    import org.kuali.common.jdbc.listener.SqlListener;
032    import org.kuali.common.jdbc.supplier.SqlSupplier;
033    import org.kuali.common.util.CollectionUtils;
034    import org.kuali.common.util.LocationUtils;
035    import org.kuali.common.util.Str;
036    import org.kuali.common.util.nullify.NullUtils;
037    import org.kuali.common.util.spring.SpringUtils;
038    import org.slf4j.Logger;
039    import org.slf4j.LoggerFactory;
040    import org.springframework.core.env.Environment;
041    import org.springframework.util.Assert;
042    
043    /**
044     * @deprecated
045     */
046    @Deprecated
047    public class SqlConfigUtils {
048    
049        private static final Logger logger = LoggerFactory.getLogger(SqlConfigUtils.class);
050    
051            public static final String SQL_ORDER_KEY = "sql.execution.order";
052            public static final String RESOURCES_SUFFIX = ".resources";
053    
054        public static final String SQL_NAMESPACE_TOKEN = "sql";
055    
056        protected static final String SKIP_PROPERTY_KEY_SUFFIX = ".skip";
057    
058        protected static final String SKIP_EXECUTABLE_PROPERTY_KEY_SUFFIX = ".executable.skip";
059    
060        protected static final String TRACK_PROGRESS_KEY_SUFFIX = ".trackProgressByUpdateCount";
061    
062        public static JdbcExecutable getJdbcExecutable(SqlConfigContext scc) {
063                    String skipKey = scc.getContext().getGroup() + SKIP_EXECUTABLE_PROPERTY_KEY_SUFFIX;
064    
065                    JdbcContext context = getJdbcContext(scc);
066                    context.setListener(getSqlListener(scc.getContext().getMode()));
067    
068                    JdbcExecutable exec = new JdbcExecutable();
069                    exec.setSkip(SpringUtils.getBoolean(scc.getEnv(), skipKey, false));
070                    exec.setService(scc.getCommonConfig().jdbcService());
071                    exec.setContext(context);
072                    return exec;
073            }
074    
075            public static SqlListener getSqlListener(SqlMode mode) {
076                    switch (mode) {
077                    case CONCURRENT:
078                            return new LogSqlListener();
079                    case SEQUENTIAL:
080                            List<SqlListener> listeners = new ArrayList<SqlListener>();
081                            listeners.add(new LogSqlListener());
082                            listeners.add(new ProgressListener());
083                            return new NotifyingListener(listeners);
084                    default:
085                            throw new IllegalArgumentException("mode [" + mode.name() + "] is unknown");
086                    }
087            }
088    
089            public static JdbcContext getJdbcContext(SqlConfigContext scc) {
090                    SqlExecutionContext ctx = scc.getContext();
091                    SqlMode mode = ctx.getMode();
092                    switch (mode) {
093                    case CONCURRENT:
094                            return getConcurrentJdbcContext(scc);
095                    case SEQUENTIAL:
096                            return getSequentialJdbcContext(scc);
097                    default:
098                            throw new IllegalArgumentException("mode [" + mode.name() + "] is unknown");
099                    }
100            }
101    
102            public static List<SqlExecutionContext> getSqlExecutionContexts(Environment env) {
103                    // Extract the CSV value for "sql.execution.order" from the environment
104                    String csv = SpringUtils.getProperty(env, SQL_ORDER_KEY);
105    
106                    // NONE or NULL means there is no sql to execute
107                    if (NullUtils.isNullOrNone(csv)) {
108                            csv = Str.EMPTY_STRING;
109                    }
110    
111                    // Convert the CSV to a list of property keys
112                    List<String> propertyKeys = CollectionUtils.getTrimmedListFromCSV(csv);
113    
114                    // Validate that the property keys referenced by sql.execution.order actually exist
115                    // Validate that all of the resources referenced by any of the properties also exist
116                    validateSqlExecutionOrderValues(env, propertyKeys);
117    
118                    // Convert the text values into pojo's
119                    return getSqlExecutionContexts(propertyKeys);
120            }
121    
122            public static void validateSqlExecutionOrderValues(Environment env, List<String> propertykeys) {
123    
124                    // Go through the list of keys and check each one
125                    for (String propertyKey : propertykeys) {
126    
127                            // This is a CSV list of actual resources (or .resources files) to load
128                            String csv = SpringUtils.getProperty(env, propertyKey);
129    
130                            // Extract the CSV list of resources referenced by this property key
131                            List<String> resources = CollectionUtils.getTrimmedListFromCSV(csv);
132    
133                            // Validate that all of the resources exist and that all of the locations referenced by any .resources files also exist
134                            validateResources(resources);
135                    }
136            }
137    
138            public static void validateResources(List<String> resources) {
139                    // Validate that every resource exists
140                    for (String resource : resources) {
141    
142                            // Make sure the .resources file exists
143                            LocationUtils.validateLocation(resource);
144    
145                            // Make sure each resource inside the .resources file exists
146                            if (StringUtils.endsWithIgnoreCase(resource, RESOURCES_SUFFIX)) {
147                                    LocationUtils.validateLocationListing(resource);
148                            }
149                    }
150            }
151    
152            public static List<SqlExecutionContext> getSqlExecutionContexts(List<String> propertyKeys) {
153    
154                    // Setup some storage for the contexts
155                    List<SqlExecutionContext> contexts = new ArrayList<SqlExecutionContext>();
156    
157                    // Each property key will be something like "sql.schema.concurrent" where the last 2 tokens represent the "group" and the "execution mode"
158                    for (String propertyKey : propertyKeys) {
159    
160                            // properties are assumed to have the following structure:
161                // "sql." + any user string + "." + valid SqlMode
162                // ex.  "sql.foo.bar.concurrent"
163                            String[] tokens = StringUtils.split(propertyKey, Str.DOT);
164    
165                            // Must have at least 3 tokens
166                            Assert.isTrue(tokens.length >= 3, "tokens.length < 3");
167    
168                // These are the indexes corresponding to the tokens that indicate sql prefix and mode
169                int sqlTokenIndex = 0;
170                            int modeIndex = tokens.length - 1;
171    
172                Assert.isTrue(tokens[sqlTokenIndex].equals(SQL_NAMESPACE_TOKEN), "sql execution properties must start with a 'sql.' namespace");
173    
174                StringBuilder sb = new StringBuilder();
175                boolean first = true;
176                for(int i = 0; i < tokens.length; i++) {
177    
178                    if (i != modeIndex) {
179                        // append a dot, but only if at least one token has been appended
180                        if(first) {
181                            first = false;
182                        }
183                        else {
184                            sb.append(Str.DOT);
185                        }
186    
187                        // append the token
188                        sb.append(tokens[i]);
189                    }
190                }
191    
192                            // Extract the group. This can be any arbitrary text that indicates some kind of SQL grouping, eg "schema", "data", "other"
193                String group = sb.toString();
194    
195                            // Extract the mode and convert to upper case
196                            // Only values allowed here are "sequential" and "concurrent"
197                            String modeString = StringUtils.trim(tokens[modeIndex].toUpperCase());
198    
199                            // Convert the mode string to a strongly typed object
200                            SqlMode mode = SqlMode.valueOf(modeString);
201    
202                            // Create a new context and add it to the list
203                            contexts.add(new SqlExecutionContext(propertyKey, group, mode));
204                    }
205    
206                    // Return the list we created
207                    return contexts;
208            }
209    
210            public static JdbcContext getConcurrentJdbcContext(SqlConfigContext rcc) {
211                    String threads = SpringUtils.getProperty(rcc.getEnv(), "sql.threads");
212                    JdbcContext ctx = getBaseJdbcContext(rcc);
213                    ctx.setMultithreaded(true);
214                    ctx.setThreads(new Integer(threads));
215                    return ctx;
216            }
217    
218            public static JdbcContext getSequentialJdbcContext(SqlConfigContext rcc) {
219                    JdbcContext ctx = getBaseJdbcContext(rcc);
220                    ctx.setMultithreaded(false);
221                    ctx.setThreads(1);
222                    return ctx;
223            }
224    
225            protected static JdbcContext getBaseJdbcContext(SqlConfigContext scc) {
226                    SqlExecutionContext sec = scc.getContext();
227                    // dba, schema, data, constraints, other
228                    String group = sec.getGroup();
229                    // sql.dba.concurrent, sql.dba.sequential, sql.schema.concurrent, sql.schema.sequential, etc
230                    String propertyKey = scc.getContext().getKey();
231            String skipKey = group + SKIP_PROPERTY_KEY_SUFFIX;
232            String trackProgressKey = propertyKey + TRACK_PROGRESS_KEY_SUFFIX;
233                    String message = "[" + sec.getGroup() + ":" + sec.getMode().name().toLowerCase() + "]";
234    
235    
236            boolean skip = SpringUtils.getBoolean(scc.getEnv(), skipKey, false);
237                    boolean trackProgressByUpdateCount = SpringUtils.getBoolean(scc.getEnv(), trackProgressKey, false);
238                    logger.debug("{}={}", trackProgressKey, trackProgressByUpdateCount);
239                    List<SqlSupplier> suppliers = scc.getCommonConfig().getSqlSuppliers(propertyKey);
240                    DataSource dataSource = scc.getDataSourceConfig().jdbcDataSource();
241    
242                    JdbcContext ctx = new JdbcContext();
243                    ctx.setMessage(message);
244                    ctx.setSkip(skip);
245                    ctx.setDataSource(dataSource);
246                    ctx.setTrackProgressByUpdateCount(trackProgressByUpdateCount);
247                    ctx.setSuppliers(suppliers);
248                    return ctx;
249            }
250    
251    }