1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.common.jdbc;
17
18 import java.util.ArrayList;
19 import java.util.Arrays;
20 import java.util.Date;
21 import java.util.List;
22 import java.util.Properties;
23
24 import org.apache.commons.lang3.StringUtils;
25 import org.kuali.common.jdbc.context.DatabaseProcessContext;
26 import org.kuali.common.jdbc.context.DatabaseResetContext;
27 import org.kuali.common.jdbc.context.ExecutionContext;
28 import org.kuali.common.jdbc.listener.BucketListener;
29 import org.kuali.common.jdbc.listener.LogSqlListener;
30 import org.kuali.common.jdbc.listener.NotifyingListener;
31 import org.kuali.common.jdbc.listener.ProgressListener;
32 import org.kuali.common.jdbc.listener.SqlListener;
33 import org.kuali.common.jdbc.listener.SummaryListener;
34 import org.kuali.common.util.CollectionUtils;
35 import org.kuali.common.util.FormatUtils;
36 import org.kuali.common.util.LocationUtils;
37 import org.kuali.common.util.LoggerLevel;
38 import org.kuali.common.util.LoggerUtils;
39 import org.kuali.common.util.nullify.NullUtils;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
42
43 public class DefaultDatabaseService implements DatabaseService {
44 private static final Logger logger = LoggerFactory.getLogger(DefaultDatabaseService.class);
45 private static final String CONCURRENT = "concurrent";
46 private static final String SEQUENTIAL = "sequential";
47 private static final String MESSAGE = "message";
48 private static final String LIST_SUFFIX = ".list";
49 private static final String ORDER = "order";
50
51 @Override
52 public void reset(DatabaseResetContext context) {
53 DatabaseProcessContext dpc = context.getDatabaseProcessContext();
54 logger.info("------------------------------------------------------------------------");
55 logger.info("Reset Database - {}", context.getDatabaseProcessContext().getUrl());
56 logger.info("------------------------------------------------------------------------");
57 logger.info("Vendor - {}", context.getDatabaseProcessContext().getVendor());
58 logger.info("URL - {}", context.getDatabaseProcessContext().getUrl());
59 logger.info("User - {}", LoggerUtils.getUsername(dpc.getUsername()));
60 logger.info("Password - {}", LoggerUtils.getPassword(dpc.getUsername(), dpc.getPassword()));
61 logger.info("DBA URL - {}", context.getDatabaseProcessContext().getDbaUrl());
62 logger.info("DBA User - {}", LoggerUtils.getUsername(dpc.getDbaUsername()));
63 logger.info("DBA Password - {}", LoggerUtils.getPassword(dpc.getDbaUsername(), dpc.getDbaPassword()));
64 JdbcMetaData metadata = context.getService().getJdbcMetaData(context.getDbaJdbcContext().getDataSource());
65 logger.info("Product Name - {}", metadata.getDatabaseProductName());
66 logger.info("Product Version - {}", metadata.getDatabaseProductVersion());
67 logger.info("Driver - {}", context.getDatabaseProcessContext().getDriver());
68 logger.info("Driver Name - {}", metadata.getDriverName());
69 logger.info("Driver Version - {}", metadata.getDriverVersion());
70 logger.info("SQL Encoding - {}", context.getEncoding());
71 logger.info("------------------------------------------------------------------------");
72
73 int threads = context.getThreads();
74 List<ExecutionContext> schemas = getExecutionContexts(context.getSchemaPropertyPrefix(), threads, context.getProperties());
75 List<ExecutionContext> data = getExecutionContexts(context.getDataPropertyPrefix(), threads, context.getProperties());
76 List<ExecutionContext> constraints = getExecutionContexts(context.getConstraintPropertyPrefix(), threads, context.getProperties());
77 List<ExecutionContext> other = getExecutionContexts(context.getOtherPropertyPrefix(), threads, context.getProperties());
78
79 List<ExecutionContext> contexts = new ArrayList<ExecutionContext>();
80 contexts.addAll(schemas);
81 for (ExecutionContext schema : schemas) {
82 schema.setListener(getDDLListener());
83 }
84 contexts.addAll(data);
85 for (ExecutionContext ec : data) {
86 ec.setListener(getDMLListener());
87 }
88 contexts.addAll(constraints);
89 for (ExecutionContext ec : constraints) {
90 ec.setListener(getDDLListener());
91 }
92
93 contexts.addAll(other);
94 for (ExecutionContext ec : other) {
95 ec.setListener(getDDLListener());
96 }
97
98 JdbcService service = new DefaultJdbcService();
99 ExecutionContext dba = getDbaContext(context);
100 dba.setExecute(context.isExecuteSql());
101 dba.setReader(context.getDbaReader());
102
103 long start = System.currentTimeMillis();
104 service.executeSql(dba);
105 for (ExecutionContext ec : contexts) {
106 ec.setEncoding(context.getEncoding());
107 ec.setReader(context.getReader());
108 ec.setJdbcContext(context.getNormalJdbcContext());
109 ec.setExecute(context.isExecuteSql());
110 service.executeSql(ec);
111 }
112 logger.info("------------------------------------------------------------------------");
113 logger.info("Database Reset Completed");
114 logger.info("------------------------------------------------------------------------");
115 logger.info("Total time: {}", FormatUtils.getTime(System.currentTimeMillis() - start));
116 logger.info("Finished at: {}", new Date());
117 logger.info("------------------------------------------------------------------------");
118 }
119
120 protected ExecutionContext getDbaContext(DatabaseResetContext context) {
121 ExecutionContext ec = new ExecutionContext();
122 ec.setMessage("Executing DBA SQL");
123 ec.setJdbcContext(context.getDbaJdbcContext());
124 ec.setReader(context.getReader());
125 ec.setSql(Arrays.asList(context.getDbaSql()));
126 ec.setListener(getDbaListener());
127 return ec;
128 }
129
130 protected NotifyingListener getDefaultListener(boolean showRate) {
131 List<SqlListener> listeners = new ArrayList<SqlListener>();
132 listeners.add(new ProgressListener());
133 listeners.add(new SummaryListener(showRate));
134 return new NotifyingListener(listeners);
135 }
136
137 protected NotifyingListener getDDLListener() {
138 return getDefaultListener(false);
139 }
140
141 protected NotifyingListener getDMLListener() {
142 NotifyingListener listener = getDefaultListener(true);
143 listener.getListeners().add(new BucketListener());
144 return listener;
145 }
146
147 protected List<String> getLocationsFromCSV(String csv, String listSuffixPattern, Properties properties) {
148
149 List<String> keys = CollectionUtils.getTrimmedListFromCSV(csv);
150
151
152 List<String> locations = new ArrayList<String>();
153
154
155 for (String key : keys) {
156
157
158 String value = properties.getProperty(key);
159
160
161 if (value == null) {
162 throw new IllegalArgumentException("Could not locate a value for [" + key + "]");
163 }
164
165
166 if (NullUtils.isNullOrNone(value)) {
167 continue;
168 }
169
170
171 if (StringUtils.endsWith(key, listSuffixPattern)) {
172
173 locations.addAll(LocationUtils.getLocations(value));
174 } else {
175
176 locations.add(value);
177 }
178 }
179
180
181 return locations;
182 }
183
184 protected List<ExecutionContext> getExecutionContexts(String prefix, int threads, Properties properties) {
185
186 String concurrent = properties.getProperty(prefix + "." + CONCURRENT);
187 String sequential = properties.getProperty(prefix + "." + SEQUENTIAL);
188
189 String concurrentMsg = properties.getProperty(prefix + "." + CONCURRENT + "." + MESSAGE);
190 String sequentialMsg = properties.getProperty(prefix + "." + SEQUENTIAL + "." + MESSAGE);
191
192 List<String> concurrentLocations = getLocationsFromCSV(concurrent, LIST_SUFFIX, properties);
193 List<String> sequentialLocations = getLocationsFromCSV(sequential, LIST_SUFFIX, properties);
194
195 validateExists(concurrentLocations);
196 validateExists(sequentialLocations);
197
198 String order = properties.getProperty(prefix + "." + ORDER);
199 if (order == null) {
200 order = CONCURRENT + "," + SEQUENTIAL;
201 }
202 List<String> orderings = CollectionUtils.getTrimmedListFromCSV(order);
203 if (orderings.size() != ExecutionMode.values().length) {
204 throw new IllegalArgumentException("Only valid values for ordering are " + ExecutionMode.CONCURRENT + " and " + ExecutionMode.SEQUENTIAL);
205 }
206
207 ExecutionMode one = ExecutionMode.valueOf(orderings.get(0).toUpperCase());
208 ExecutionMode two = ExecutionMode.valueOf(orderings.get(1).toUpperCase());
209
210
211 if (one.equals(two)) {
212 throw new IllegalArgumentException(getInvalidOrderingMessage(order));
213 }
214
215 List<ExecutionContext> contexts = new ArrayList<ExecutionContext>();
216 ExecutionContext context1 = new ExecutionContext();
217 ExecutionContext context2 = new ExecutionContext();
218
219 if (one.equals(ExecutionMode.CONCURRENT)) {
220
221 context1.setLocations(concurrentLocations);
222 context1.setThreads(threads);
223 context1.setMessage(concurrentMsg);
224 context2.setLocations(sequentialLocations);
225 context2.setMessage(sequentialMsg);
226 } else {
227
228 context1.setLocations(sequentialLocations);
229 context1.setMessage(sequentialMsg);
230 context2.setLocations(concurrentLocations);
231 context2.setMessage(concurrentMsg);
232 context2.setThreads(threads);
233 }
234
235
236 if (!CollectionUtils.isEmpty(context1.getLocations())) {
237 contexts.add(context1);
238 }
239
240
241 if (!CollectionUtils.isEmpty(context2.getLocations())) {
242 contexts.add(context2);
243 }
244
245
246 return contexts;
247 }
248
249 protected NotifyingListener getDbaListener() {
250 LogSqlListener lsl = new LogSqlListener();
251 lsl.setLevel(LoggerLevel.INFO);
252 lsl.setFlatten(true);
253 List<SqlListener> listeners = new ArrayList<SqlListener>();
254 listeners.add(lsl);
255 listeners.add(new SummaryListener(false));
256 return new NotifyingListener(listeners);
257 }
258
259 protected void validateExists(List<String> locations) {
260 for (String location : locations) {
261 if (!LocationUtils.exists(location)) {
262 throw new IllegalArgumentException(location + " does not exist");
263 }
264 }
265 }
266
267 protected String getInvalidOrderingMessage(String order) {
268 StringBuilder sb = new StringBuilder();
269 sb.append("Ordering [" + order + "] is invalid. ");
270 sb.append("Ordering must be provided as either [" + ExecutionMode.CONCURRENT + "," + ExecutionMode.SEQUENTIAL + "] or ");
271 sb.append("[" + ExecutionMode.CONCURRENT + "," + ExecutionMode.SEQUENTIAL + "]");
272 return sb.toString();
273 }
274
275 }