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.SchemaExtractionContext;
34 import org.kuali.common.impex.schema.service.SchemaExtractionService;
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.PercentCompleteInformer;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
45
46 public class DefaultSchemaExtractionService implements SchemaExtractionService {
47
48 private static Logger log = LoggerFactory.getLogger(DefaultSchemaExtractionService.class);
49
50 protected static final int SINGLE_THREAD_COUNT = 1;
51
52 @Override
53 public Schema getSchema(SchemaExtractionContext context) {
54
55 Schema result;
56
57 try {
58 if (context.getThreadCount() <= SINGLE_THREAD_COUNT) {
59 result = extractSingleThreaded(context);
60 } else {
61 result = extractMultiThreaded(context);
62 }
63 } catch (SQLException e) {
64 throw new IllegalStateException("Unexpected SQL error", e);
65 }
66
67 sortSchemaElements(result);
68
69 return result;
70 }
71
72 protected void sortSchemaElements(Schema result) {
73 Collections.sort(result.getTables(), NamedElementComparator.getInstance());
74 Collections.sort(result.getForeignKeys(), NamedElementComparator.getInstance());
75 Collections.sort(result.getSequences(), NamedElementComparator.getInstance());
76 Collections.sort(result.getViews(), NamedElementComparator.getInstance());
77 }
78
79 protected Schema extractSingleThreaded(SchemaExtractionContext context) throws SQLException {
80 long startTime = System.currentTimeMillis();
81 log.info("Single threaded schema extraction started");
82
83 Schema result = new Schema();
84
85 List<String> tableNames = getTableNames(context);
86 log.debug("Extracting {} tables...", new Object[] { tableNames.size() });
87 result.getTables().addAll(extractTables(tableNames, context));
88 log.debug("Table extraction complete.");
89
90 result.getViews().addAll(extractViews(context));
91 log.debug("View extraction complete");
92
93 result.getSequences().addAll(extractSequences(context));
94 log.debug("Sequence extraction complete");
95
96 result.getForeignKeys().addAll(extractForeignKeys(tableNames, context));
97 log.debug("Foreign Key extraction complete");
98
99 String timeString = FormatUtils.getTime(System.currentTimeMillis() - startTime);
100 log.info("Single threaded schema extraction complete - Time: {}", new Object[] { timeString });
101 return result;
102
103 }
104
105 protected Schema extractMultiThreaded(SchemaExtractionContext context) throws SQLException {
106 log.info("Multi threaded schema extraction started");
107
108 List<String> tableNames = getTableNames(context);
109
110
111
112 int totalTasks = tableNames.size();
113
114
115 totalTasks += tableNames.size();
116
117
118 totalTasks++;
119
120
121 PercentCompleteInformer progressTracker = new PercentCompleteInformer();
122 progressTracker.setTotal(totalTasks);
123
124 Schema schema = new Schema();
125
126
127 int maxTableThreads = context.getThreadCount() - 1;
128
129 List<List<String>> splitNames = CollectionUtils.splitEvenly(tableNames, maxTableThreads);
130
131 List<ExtractSchemaBucket> schemaBuckets = new ArrayList<ExtractSchemaBucket>(splitNames.size() + 1);
132
133
134 ExtractSchemaBucket viewSequenceBucket = new ExtractViewsAndSequencesBucket();
135 viewSequenceBucket.setContext(context);
136 viewSequenceBucket.setSchema(schema);
137 schemaBuckets.add(viewSequenceBucket);
138
139
140 for (List<String> names : splitNames) {
141 ExtractSchemaBucket bucket = new ExtractSchemaBucket();
142 bucket.setTableNames(names);
143 bucket.setContext(context);
144 bucket.setSchema(schema);
145
146 schemaBuckets.add(bucket);
147 }
148
149
150
151 ThreadHandlerContext<ExtractSchemaBucket> thc = new ThreadHandlerContext<ExtractSchemaBucket>();
152 thc.setList(schemaBuckets);
153 thc.setHandler(new ExtractSchemaBucketHandler(this));
154 thc.setMax(schemaBuckets.size());
155 thc.setMin(schemaBuckets.size());
156 thc.setDivisor(1);
157
158
159 ExecutionStatistics stats = new ThreadInvoker().invokeThreads(thc);
160
161 String time = FormatUtils.getTime(stats.getExecutionTime());
162 log.info("Schema extraction completed. Time: {}", time);
163
164 return schema;
165 }
166
167 @Override
168 public List<Table> extractTables(List<String> tableNames, SchemaExtractionContext context) throws SQLException {
169 List<Table> results = new ArrayList<Table>(tableNames.size());
170
171 DatabaseMetaData metaData = getMetaDataInstance(context);
172
173 try {
174 for (String name : tableNames) {
175 results.add(extractTable(name, context.getSchemaName(), metaData));
176 }
177
178 return results;
179 } finally {
180 JdbcUtils.closeQuietly(context.getDataSource(), metaData.getConnection());
181 }
182 }
183
184 protected Table extractTable(String tablename, String schemaName, DatabaseMetaData metaData) throws SQLException {
185 Table result = new Table(tablename);
186
187 result.setDescription(ExtractionUtils.extractTableComment(tablename, schemaName, metaData));
188 result.setColumns(ExtractionUtils.extractTableColumns(tablename, schemaName, metaData));
189
190 List<Index> allTableIndices = ExtractionUtils.extractTableIndices(tablename, schemaName, metaData);
191
192 for (Index index : allTableIndices) {
193 if (index.isUnique()) {
194 UniqueConstraint u = new UniqueConstraint(index.getColumnNames(), index.getName());
195 result.getUniqueConstraints().add(u);
196 } else {
197 result.getIndices().add(index);
198 }
199 }
200 return result;
201
202 }
203
204 protected List<String> getTableNames(SchemaExtractionContext context) throws SQLException {
205 DatabaseMetaData metaData = getMetaDataInstance(context);
206
207 List<String> allTables;
208 try {
209 allTables = ExtractionUtils.getTableNamesFromMetaData(context.getSchemaName(), metaData);
210 } finally {
211 JdbcUtils.closeQuietly(context.getDataSource(), metaData.getConnection());
212 }
213
214 List<String> filteredNames = new ArrayList<String>();
215 for (String name : allTables) {
216 if (context.getNameFilter().include(name)) {
217 filteredNames.add(name);
218 }
219 }
220 return filteredNames;
221 }
222
223 @Override
224 public List<View> extractViews(SchemaExtractionContext context) throws SQLException {
225 Connection connection = context.getDataSource().getConnection();
226 try {
227 return context.getViewFinder().findViews(connection, context.getSchemaName(), context.getNameFilter());
228 } finally {
229 JdbcUtils.closeQuietly(context.getDataSource(), connection);
230 }
231 }
232
233 @Override
234 public List<Sequence> extractSequences(SchemaExtractionContext context) throws SQLException {
235 Connection connection = context.getDataSource().getConnection();
236 try {
237 return context.getSequenceFinder().findSequences(connection, context.getSchemaName(), context.getNameFilter());
238 } finally {
239 JdbcUtils.closeQuietly(context.getDataSource(), connection);
240 }
241 }
242
243 @Override
244 public List<ForeignKey> extractForeignKeys(List<String> tableNames, SchemaExtractionContext context) throws SQLException {
245 DatabaseMetaData metaData = getMetaDataInstance(context);
246 try {
247 return ExtractionUtils.extractForeignKeys(tableNames, context.getSchemaName(), metaData);
248 } finally {
249 JdbcUtils.closeQuietly(context.getDataSource(), metaData.getConnection());
250 }
251 }
252
253 protected DatabaseMetaData getMetaDataInstance(SchemaExtractionContext context) throws SQLException {
254 return context.getDataSource().getConnection().getMetaData();
255 }
256 }