1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.common.impex.schema.service.impl;
17
18 import java.sql.Connection;
19 import java.sql.DatabaseMetaData;
20 import java.sql.SQLException;
21 import java.util.ArrayList;
22 import java.util.Collections;
23 import java.util.List;
24
25 import org.kuali.common.impex.model.ForeignKey;
26 import org.kuali.common.impex.model.Index;
27 import org.kuali.common.impex.model.Schema;
28 import org.kuali.common.impex.model.Sequence;
29 import org.kuali.common.impex.model.Table;
30 import org.kuali.common.impex.model.UniqueConstraint;
31 import org.kuali.common.impex.model.View;
32 import org.kuali.common.impex.model.util.NamedElementComparator;
33 import org.kuali.common.impex.schema.service.ExtractSchemaContext;
34 import org.kuali.common.impex.schema.service.ExtractSchemaService;
35 import org.kuali.common.impex.util.ExtractionUtils;
36 import org.kuali.common.jdbc.JdbcUtils;
37 import org.kuali.common.threads.ExecutionStatistics;
38 import org.kuali.common.threads.ThreadHandlerContext;
39 import org.kuali.common.threads.ThreadInvoker;
40 import org.kuali.common.util.CollectionUtils;
41 import org.kuali.common.util.FormatUtils;
42 import org.kuali.common.util.LoggerUtils;
43 import org.kuali.common.util.PercentCompleteInformer;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
46
47 public class DefaultExtractSchemaService implements ExtractSchemaService {
48
49 private static Logger logger = LoggerFactory.getLogger(DefaultExtractSchemaService.class);
50
51 protected static final int SINGLE_THREAD_COUNT = 1;
52
53 @Override
54 public Schema getSchema(ExtractSchemaContext context) {
55
56 Schema schema = extractSchema(context);
57
58
59 sortSchemaElements(schema);
60
61
62 return schema;
63 }
64
65 protected Schema extractSchema(ExtractSchemaContext context) {
66 try {
67
68 if (context.getThreadCount() <= SINGLE_THREAD_COUNT) {
69 return extractSingleThreaded(context);
70 } else {
71 return extractMultiThreaded(context);
72 }
73 } catch (SQLException e) {
74 throw new IllegalStateException("Unexpected SQL error", e);
75 }
76 }
77
78 protected Schema extractMultiThreaded(ExtractSchemaContext context) throws SQLException {
79
80 long start = System.currentTimeMillis();
81
82 logger.info("[schema:extract:starting]");
83
84
85
86 List<String> tableNames = getTableNames(context);
87
88
89
90 int totalTasks = tableNames.size();
91
92
93 totalTasks += tableNames.size();
94
95
96 totalTasks += 2;
97
98
99 PercentCompleteInformer informer = new PercentCompleteInformer();
100 informer.setTotal(totalTasks);
101 context.setInformer(informer);
102
103
104 int maxTableThreads = context.getThreadCount() - 1;
105
106
107 List<List<String>> splitNames = CollectionUtils.splitEvenly(tableNames, maxTableThreads);
108
109
110 List<ExtractSchemaBucket> schemaBuckets = new ArrayList<ExtractSchemaBucket>();
111
112
113 Schema schema = new Schema();
114
115
116 ExtractSchemaBucket viewSequenceBucket = new ExtractViewsAndSequencesBucket();
117 viewSequenceBucket.setContext(context);
118 viewSequenceBucket.setSchema(schema);
119 viewSequenceBucket.setInformer(informer);
120 schemaBuckets.add(viewSequenceBucket);
121
122
123 for (List<String> names : splitNames) {
124 ExtractSchemaBucket bucket = new ExtractSchemaBucket();
125 bucket.setTableNames(names);
126 bucket.setContext(context);
127 bucket.setSchema(schema);
128 bucket.setInformer(informer);
129
130 schemaBuckets.add(bucket);
131 }
132
133
134 ThreadHandlerContext<ExtractSchemaBucket> thc = new ThreadHandlerContext<ExtractSchemaBucket>();
135 thc.setList(schemaBuckets);
136 thc.setHandler(new ExtractSchemaBucketHandler(this));
137 thc.setMax(schemaBuckets.size());
138 thc.setMin(schemaBuckets.size());
139 thc.setDivisor(1);
140
141
142 logger.info("[schema:extract:metadata:starting]");
143 informer.start();
144
145 ExecutionStatistics stats = new ThreadInvoker().invokeThreads(thc);
146 informer.stop();
147 logger.info("[schema:extract:metadata:complete] - {}", FormatUtils.getTime(stats.getExecutionTime()));
148 logger.info("[schema:extract:complete] - {}", FormatUtils.getTime(System.currentTimeMillis() - start));
149 return schema;
150 }
151
152 @Override
153 public List<Table> extractTables(List<String> tableNames, ExtractSchemaContext context) throws SQLException {
154 List<Table> results = new ArrayList<Table>(tableNames.size());
155
156 DatabaseMetaData metaData = getMetaDataInstance(context);
157
158 try {
159 for (String name : tableNames) {
160 results.add(extractTable(name, context.getSchemaName(), metaData));
161 context.getInformer().incrementProgress();
162 }
163
164 return results;
165 } finally {
166 JdbcUtils.closeQuietly(context.getDataSource(), metaData.getConnection());
167 }
168 }
169
170 protected Table extractTable(String tablename, String schemaName, DatabaseMetaData metaData) throws SQLException {
171 Table result = new Table(tablename);
172
173 result.setDescription(ExtractionUtils.extractTableComment(tablename, schemaName, metaData));
174 result.setColumns(ExtractionUtils.extractTableColumns(tablename, schemaName, metaData));
175
176 List<Index> allTableIndices = ExtractionUtils.extractTableIndices(tablename, schemaName, metaData);
177
178 for (Index index : allTableIndices) {
179 if (index.isUnique()) {
180 UniqueConstraint u = new UniqueConstraint(index.getColumnNames(), index.getName());
181 result.getUniqueConstraints().add(u);
182 } else {
183 result.getIndices().add(index);
184 }
185 }
186 return result;
187
188 }
189
190 protected List<String> getTableNames(ExtractSchemaContext context) throws SQLException {
191 long start = System.currentTimeMillis();
192 logger.info("[schema:extract:tablenames] - {}", LoggerUtils.getLogMsg(context.getNameFilter()));
193 List<String> tableNames = ExtractionUtils.getTableNames(context.getDataSource(), context.getSchemaName());
194 List<String> excluded = CollectionUtils.filterAndSort(tableNames, context.getNameFilter());
195 String time = FormatUtils.getTime(System.currentTimeMillis() - start);
196 String original = FormatUtils.getCount(tableNames.size() + excluded.size());
197 String filtered = FormatUtils.getCount(tableNames.size());
198 if (!CollectionUtils.isEmpty(excluded)) {
199 logger.info(" excluded -> [{}]", CollectionUtils.getSpaceSeparatedCSV(excluded));
200 }
201 Object[] args = { original, filtered, time };
202 logger.info("[schema:extract:tablenames] - [all: {} filtered: {}] - {}", args);
203 return tableNames;
204 }
205
206 @Override
207 public List<View> extractViews(ExtractSchemaContext context) throws SQLException {
208 Connection connection = context.getDataSource().getConnection();
209 try {
210 return context.getViewFinder().findViews(connection, context.getSchemaName(), context.getNameFilter());
211 } finally {
212 JdbcUtils.closeQuietly(context.getDataSource(), connection);
213 }
214 }
215
216 @Override
217 public List<Sequence> extractSequences(ExtractSchemaContext context) throws SQLException {
218 Connection connection = context.getDataSource().getConnection();
219 try {
220 return context.getSequenceFinder().findSequences(connection, context.getSchemaName(), context.getNameFilter());
221 } finally {
222 JdbcUtils.closeQuietly(context.getDataSource(), connection);
223 }
224 }
225
226 @Override
227 public List<ForeignKey> extractForeignKeys(List<String> tableNames, ExtractSchemaContext context) throws SQLException {
228 DatabaseMetaData meta = getMetaDataInstance(context);
229 try {
230 return ExtractionUtils.extractForeignKeys(meta, context.getSchemaName(), tableNames, context.getInformer());
231 } finally {
232 JdbcUtils.closeQuietly(context.getDataSource(), meta.getConnection());
233 }
234 }
235
236
237
238
239 protected void sortSchemaElements(Schema schema) {
240 Collections.sort(schema.getTables(), NamedElementComparator.getInstance());
241 Collections.sort(schema.getForeignKeys(), NamedElementComparator.getInstance());
242 Collections.sort(schema.getSequences(), NamedElementComparator.getInstance());
243 Collections.sort(schema.getViews(), NamedElementComparator.getInstance());
244 }
245
246 protected DatabaseMetaData getMetaDataInstance(ExtractSchemaContext context) throws SQLException {
247 return context.getDataSource().getConnection().getMetaData();
248 }
249
250 protected Schema extractSingleThreaded(ExtractSchemaContext context) throws SQLException {
251 long startTime = System.currentTimeMillis();
252 logger.info("Single threaded schema extraction started");
253
254 Schema result = new Schema();
255
256 List<String> tableNames = getTableNames(context);
257 logger.debug("Extracting {} tables...", new Object[] { tableNames.size() });
258 result.getTables().addAll(extractTables(tableNames, context));
259 logger.debug("Table extraction complete.");
260
261 result.getViews().addAll(extractViews(context));
262 logger.debug("View extraction complete");
263
264 result.getSequences().addAll(extractSequences(context));
265 logger.debug("Sequence extraction complete");
266
267 result.getForeignKeys().addAll(extractForeignKeys(tableNames, context));
268 logger.debug("Foreign Key extraction complete");
269
270 String timeString = FormatUtils.getTime(System.currentTimeMillis() - startTime);
271 logger.info("Single threaded schema extraction complete - Time: {}", new Object[] { timeString });
272 return result;
273
274 }
275
276 }