| 1 | package org.jcoderz.testdata; |
|---|
| 2 | |
|---|
| 3 | import java.io.File; |
|---|
| 4 | import java.io.IOException; |
|---|
| 5 | import java.io.InputStream; |
|---|
| 6 | import java.sql.Connection; |
|---|
| 7 | import java.sql.DriverManager; |
|---|
| 8 | import java.sql.ResultSet; |
|---|
| 9 | import java.sql.SQLException; |
|---|
| 10 | import java.sql.SQLWarning; |
|---|
| 11 | import java.sql.Statement; |
|---|
| 12 | import java.util.ArrayList; |
|---|
| 13 | import java.util.Collection; |
|---|
| 14 | import java.util.HashMap; |
|---|
| 15 | import java.util.HashSet; |
|---|
| 16 | import java.util.Iterator; |
|---|
| 17 | import java.util.List; |
|---|
| 18 | import java.util.Locale; |
|---|
| 19 | import java.util.Map; |
|---|
| 20 | import java.util.Properties; |
|---|
| 21 | import java.util.Set; |
|---|
| 22 | import java.util.logging.Level; |
|---|
| 23 | import java.util.logging.Logger; |
|---|
| 24 | |
|---|
| 25 | import nu.xom.Builder; |
|---|
| 26 | import nu.xom.Document; |
|---|
| 27 | import nu.xom.Element; |
|---|
| 28 | import nu.xom.Node; |
|---|
| 29 | import nu.xom.Nodes; |
|---|
| 30 | import nu.xom.XPathContext; |
|---|
| 31 | import nu.xom.xinclude.XIncluder; |
|---|
| 32 | |
|---|
| 33 | import org.hibernate.SessionFactory; |
|---|
| 34 | import org.hibernate.cfg.Configuration; |
|---|
| 35 | import org.hibernate.mapping.Table; |
|---|
| 36 | |
|---|
| 37 | /** |
|---|
| 38 | * Generic Test data importer. Recursively scans a directory for test data items and |
|---|
| 39 | * submits them via JDBC to a predefined database. The tables and attribute names are |
|---|
| 40 | * mapped straightforward to the hibernate configuration expected on the classpath. This |
|---|
| 41 | * implementation relies on auto index of all tables. |
|---|
| 42 | * |
|---|
| 43 | * @author Torsten Stolpmann |
|---|
| 44 | */ |
|---|
| 45 | public class Importer { |
|---|
| 46 | |
|---|
| 47 | private static final int ERROR_EXIT_CODE = 20; |
|---|
| 48 | |
|---|
| 49 | private static final String DEFAULT_TABLE_PREFIX = "S0IR_"; |
|---|
| 50 | |
|---|
| 51 | private static final long DEFAULT_SEQUENCE_BASE = 100000L; |
|---|
| 52 | |
|---|
| 53 | private static final String SUFFIX_XML = ".xml"; |
|---|
| 54 | |
|---|
| 55 | private static final String FILE_SEPARATOR = "/"; |
|---|
| 56 | |
|---|
| 57 | private static final XPathContext TD_CONTEXT = new XPathContext("td", |
|---|
| 58 | "http://jcoderz.org/test-data"); |
|---|
| 59 | |
|---|
| 60 | static final Logger logger = Logger.getLogger(Importer.class.getName()); |
|---|
| 61 | |
|---|
| 62 | private String tablePrefix; |
|---|
| 63 | private long sequenceBase; |
|---|
| 64 | |
|---|
| 65 | private Connection connection; |
|---|
| 66 | |
|---|
| 67 | /** |
|---|
| 68 | * The main method. |
|---|
| 69 | * |
|---|
| 70 | * @param args Command line arguments. |
|---|
| 71 | */ |
|---|
| 72 | public static void main(String[] args) { |
|---|
| 73 | |
|---|
| 74 | if (args.length < 1) { |
|---|
| 75 | System.out.println("Usage: " + Importer.class.getName() |
|---|
| 76 | + " <sourceDirectory>"); |
|---|
| 77 | } |
|---|
| 78 | |
|---|
| 79 | String sourceDirectory = args[0]; |
|---|
| 80 | |
|---|
| 81 | Properties properties = new Properties(); |
|---|
| 82 | List<String> tableNames = new ArrayList<String>(); |
|---|
| 83 | |
|---|
| 84 | logger.info("Starting import ..."); |
|---|
| 85 | |
|---|
| 86 | Locale.setDefault(Locale.GERMANY); |
|---|
| 87 | |
|---|
| 88 | try { |
|---|
| 89 | String defaultPropertiesName = "/testdata.properties"; |
|---|
| 90 | |
|---|
| 91 | InputStream defaultInput = Importer.class |
|---|
| 92 | .getResourceAsStream(defaultPropertiesName); |
|---|
| 93 | if (defaultInput != null) { |
|---|
| 94 | logger.fine("Loading default properties (" + defaultPropertiesName + ")"); |
|---|
| 95 | properties.load(defaultInput); //$NON-NLS-1$ |
|---|
| 96 | } else { |
|---|
| 97 | logger.fine("Default properties not found"); |
|---|
| 98 | } |
|---|
| 99 | String userPropertiesName = FILE_SEPARATOR + System.getProperty("user.name") |
|---|
| 100 | + ".properties"; |
|---|
| 101 | InputStream userInput = Importer.class |
|---|
| 102 | .getResourceAsStream(userPropertiesName); |
|---|
| 103 | if (userInput != null) { |
|---|
| 104 | logger.fine("Loading user properties"); |
|---|
| 105 | properties.load(userInput); //$NON-NLS-1$ |
|---|
| 106 | } else { |
|---|
| 107 | logger.fine("User properties (" + userPropertiesName + ") not found"); |
|---|
| 108 | } |
|---|
| 109 | System.getProperties().putAll(properties); |
|---|
| 110 | logger.warning(System.getProperties().toString()); |
|---|
| 111 | properties = System.getProperties(); |
|---|
| 112 | |
|---|
| 113 | Configuration hibernateConfig = new Configuration(); |
|---|
| 114 | hibernateConfig.addProperties(properties); |
|---|
| 115 | hibernateConfig.configure(); |
|---|
| 116 | hibernateConfig.getProperties().remove("hibernate.connection.datasource"); |
|---|
| 117 | hibernateConfig.getProperties().remove( |
|---|
| 118 | "hibernate.transaction.manager_lookup_class"); |
|---|
| 119 | hibernateConfig.getProperties().remove("hibernate.transaction.factory_class"); |
|---|
| 120 | |
|---|
| 121 | Iterator<?> iter = hibernateConfig.getTableMappings(); |
|---|
| 122 | while (iter.hasNext()) { |
|---|
| 123 | Table table = (Table) iter.next(); |
|---|
| 124 | tableNames.add(table.getName().toUpperCase()); |
|---|
| 125 | } |
|---|
| 126 | SessionFactory factory = hibernateConfig.buildSessionFactory(); |
|---|
| 127 | } catch (IOException e) { |
|---|
| 128 | logger.log(Level.SEVERE, "Exception: ", e); |
|---|
| 129 | System.exit(ERROR_EXIT_CODE); |
|---|
| 130 | } |
|---|
| 131 | |
|---|
| 132 | Importer importer = new Importer(DEFAULT_TABLE_PREFIX, DEFAULT_SEQUENCE_BASE); |
|---|
| 133 | |
|---|
| 134 | Map<String, Document> items = new HashMap<String, Document>(); |
|---|
| 135 | |
|---|
| 136 | if (!importer.loadItems(sourceDirectory, items)) { |
|---|
| 137 | logger.log(Level.SEVERE, "Invalid test data set detected - aborting."); |
|---|
| 138 | System.exit(ERROR_EXIT_CODE); |
|---|
| 139 | } |
|---|
| 140 | |
|---|
| 141 | Map<String, Set<String>> dependencyMap = importer.buildDependencyMap(items); |
|---|
| 142 | |
|---|
| 143 | if (importer.validateDependencies(items, dependencyMap)) { |
|---|
| 144 | List<String> result = new ArrayList<String>(); |
|---|
| 145 | List<String> leftover = new ArrayList<String>(); |
|---|
| 146 | |
|---|
| 147 | importer.reOrderItems(items, dependencyMap, result, leftover); |
|---|
| 148 | |
|---|
| 149 | List<String> queries = new ArrayList<String>(); |
|---|
| 150 | if (importer.generateQueries(result, items, queries, null, tableNames, properties, |
|---|
| 151 | false)) { |
|---|
| 152 | logger.info("Execution plan for constrained inserts:"); |
|---|
| 153 | logger.info("---------------------------------------"); |
|---|
| 154 | for (String query : queries) { |
|---|
| 155 | logger.info(query); |
|---|
| 156 | } |
|---|
| 157 | logger.info("---------------------------------------"); |
|---|
| 158 | |
|---|
| 159 | } else { |
|---|
| 160 | logger |
|---|
| 161 | .severe("No insert statements have been executed since errors have been detected."); |
|---|
| 162 | System.exit(ERROR_EXIT_CODE); |
|---|
| 163 | } |
|---|
| 164 | |
|---|
| 165 | List<String> leftOverQueries = new ArrayList<String>(); |
|---|
| 166 | Set<String> postQueries = new HashSet<String>(); |
|---|
| 167 | |
|---|
| 168 | if (importer.generateQueries(leftover, items, leftOverQueries, postQueries, tableNames, |
|---|
| 169 | properties, true)) { |
|---|
| 170 | logger.info("Execution plan for unconstrained inserts:"); |
|---|
| 171 | logger.info("-----------------------------------------"); |
|---|
| 172 | for (String query : leftOverQueries) { |
|---|
| 173 | logger.info(query); |
|---|
| 174 | } |
|---|
| 175 | |
|---|
| 176 | for (String query : postQueries) { |
|---|
| 177 | logger.info(query); |
|---|
| 178 | } |
|---|
| 179 | logger.info("---------------------------------------"); |
|---|
| 180 | |
|---|
| 181 | } else { |
|---|
| 182 | logger |
|---|
| 183 | .severe("No insert statements have been executed since errors have been detected."); |
|---|
| 184 | System.exit(ERROR_EXIT_CODE); |
|---|
| 185 | } |
|---|
| 186 | |
|---|
| 187 | importer.executeQueries(properties, queries, false); |
|---|
| 188 | importer.executeQueries(properties, leftOverQueries, false); |
|---|
| 189 | importer.executeQueries(properties, postQueries, true); |
|---|
| 190 | |
|---|
| 191 | try { |
|---|
| 192 | Connection conn = importer.getConnection(properties); |
|---|
| 193 | conn.close(); |
|---|
| 194 | } catch (ClassNotFoundException e) { |
|---|
| 195 | logger.log(Level.SEVERE, "Exception: ", e); |
|---|
| 196 | } catch (SQLException e) { |
|---|
| 197 | logger.log(Level.SEVERE, "Exception: ", e); |
|---|
| 198 | } |
|---|
| 199 | } else { |
|---|
| 200 | logger |
|---|
| 201 | .severe("No insert statements have been executed since errors have been detected."); |
|---|
| 202 | System.exit(ERROR_EXIT_CODE); |
|---|
| 203 | } |
|---|
| 204 | } |
|---|
| 205 | |
|---|
| 206 | public Importer(final String prefix, final long base) { |
|---|
| 207 | |
|---|
| 208 | tablePrefix = prefix; |
|---|
| 209 | sequenceBase = base; |
|---|
| 210 | |
|---|
| 211 | } |
|---|
| 212 | |
|---|
| 213 | /** |
|---|
| 214 | * Execute the supplied SQL queries. |
|---|
| 215 | * |
|---|
| 216 | * @param hibernateProperties the hibernate properties. |
|---|
| 217 | * @param queries the queries |
|---|
| 218 | */ |
|---|
| 219 | private void executeQueries(final Properties hibernateProperties, |
|---|
| 220 | final Collection<String> queries, boolean delayErrors) { |
|---|
| 221 | |
|---|
| 222 | boolean success = true; |
|---|
| 223 | try { |
|---|
| 224 | Connection conn = getConnection(hibernateProperties); |
|---|
| 225 | |
|---|
| 226 | for (SQLWarning warn = conn.getWarnings(); warn != null; warn = warn |
|---|
| 227 | .getNextWarning()) { |
|---|
| 228 | logger.warning("SQL Warning:"); |
|---|
| 229 | logger.warning("State : " + warn.getSQLState()); |
|---|
| 230 | logger.warning("Message: " + warn.getMessage()); |
|---|
| 231 | logger.warning("Error : " + warn.getErrorCode()); |
|---|
| 232 | } |
|---|
| 233 | |
|---|
| 234 | Statement langStmt = conn.createStatement(); |
|---|
| 235 | String alterStatement = "ALTER SESSION SET NLS_TERRITORY='AMERICA'"; |
|---|
| 236 | langStmt.execute(alterStatement); |
|---|
| 237 | langStmt.close(); |
|---|
| 238 | |
|---|
| 239 | for (String query : queries) { |
|---|
| 240 | |
|---|
| 241 | try { |
|---|
| 242 | logger.info(query); |
|---|
| 243 | Statement stmt = conn.createStatement(); |
|---|
| 244 | ResultSet rs = stmt.executeQuery(query); |
|---|
| 245 | |
|---|
| 246 | rs.close(); |
|---|
| 247 | stmt.close(); |
|---|
| 248 | conn.commit(); |
|---|
| 249 | } catch (SQLException e) { |
|---|
| 250 | |
|---|
| 251 | logger.severe("SQL Exception:"); |
|---|
| 252 | |
|---|
| 253 | while (e != null) { |
|---|
| 254 | logger.severe("State : " + e.getSQLState()); |
|---|
| 255 | logger.severe("Message: " + e.getMessage()); |
|---|
| 256 | logger.severe("Error : " + e.getErrorCode()); |
|---|
| 257 | |
|---|
| 258 | e = e.getNextException(); |
|---|
| 259 | } |
|---|
| 260 | if (!delayErrors) { |
|---|
| 261 | System.exit(ERROR_EXIT_CODE); |
|---|
| 262 | } |
|---|
| 263 | success = false; |
|---|
| 264 | } |
|---|
| 265 | } |
|---|
| 266 | } catch (SQLException e) { |
|---|
| 267 | |
|---|
| 268 | logger.severe("SQL Exception:"); |
|---|
| 269 | |
|---|
| 270 | while (e != null) { |
|---|
| 271 | logger.severe("State : " + e.getSQLState()); |
|---|
| 272 | logger.severe("Message: " + e.getMessage()); |
|---|
| 273 | logger.severe("Error : " + e.getErrorCode()); |
|---|
| 274 | |
|---|
| 275 | e = e.getNextException(); |
|---|
| 276 | } |
|---|
| 277 | if (!delayErrors) { |
|---|
| 278 | System.exit(ERROR_EXIT_CODE); |
|---|
| 279 | } |
|---|
| 280 | success = false; |
|---|
| 281 | |
|---|
| 282 | } catch (Exception e) { |
|---|
| 283 | logger.log(Level.SEVERE, "Exception: ", e); |
|---|
| 284 | System.exit(ERROR_EXIT_CODE); |
|---|
| 285 | } |
|---|
| 286 | if (delayErrors && !success) { |
|---|
| 287 | System.exit(ERROR_EXIT_CODE); |
|---|
| 288 | } |
|---|
| 289 | } |
|---|
| 290 | |
|---|
| 291 | /** |
|---|
| 292 | * Write the map of documents. |
|---|
| 293 | * |
|---|
| 294 | * @param items the map of items |
|---|
| 295 | * @param queries the resulting queries |
|---|
| 296 | * @param postQueries the post insert queries |
|---|
| 297 | * @param idList the ordered list of item names |
|---|
| 298 | * @param tableNames the table names |
|---|
| 299 | * @param hibernateProperties the hibernate properties. |
|---|
| 300 | * @param disableConstraints true if constraints should be disabled here. |
|---|
| 301 | * @return true, if write |
|---|
| 302 | */ |
|---|
| 303 | private boolean generateQueries(final List<String> idList, |
|---|
| 304 | final Map<String, Document> items, final List<String> queries, |
|---|
| 305 | final Set<String> postQueries, |
|---|
| 306 | final List<String> tableNames, final Properties hibernateProperties, |
|---|
| 307 | final boolean disableConstraints) { |
|---|
| 308 | |
|---|
| 309 | boolean valid = true; |
|---|
| 310 | |
|---|
| 311 | final Set<String> preQueries = new HashSet<String>(); |
|---|
| 312 | final List<String> insertQueries = new ArrayList<String>(); |
|---|
| 313 | |
|---|
| 314 | for (String id : idList) { |
|---|
| 315 | |
|---|
| 316 | StringBuffer query = new StringBuffer(); |
|---|
| 317 | StringBuffer names = new StringBuffer("("); |
|---|
| 318 | StringBuffer values = new StringBuffer("("); |
|---|
| 319 | |
|---|
| 320 | Document item = items.get(id); |
|---|
| 321 | String tableName = getTableName(item); |
|---|
| 322 | |
|---|
| 323 | if (!tableNames.contains(tableName)) { |
|---|
| 324 | logger.severe("The type " + getItemType(item) + " of item " |
|---|
| 325 | + getItemId(item) |
|---|
| 326 | + " has no matching table in the current configuration (" |
|---|
| 327 | + tableName + ")"); |
|---|
| 328 | valid = false; |
|---|
| 329 | } |
|---|
| 330 | Map<String, Node> attributes = getAttributes(item); |
|---|
| 331 | |
|---|
| 332 | int position = 0; |
|---|
| 333 | int end = attributes.entrySet().size(); |
|---|
| 334 | |
|---|
| 335 | for (Map.Entry<String, Node> attribute : attributes.entrySet()) { |
|---|
| 336 | |
|---|
| 337 | position++; |
|---|
| 338 | |
|---|
| 339 | String name = attribute.getKey().toUpperCase(); |
|---|
| 340 | Node node = attribute.getValue(); |
|---|
| 341 | |
|---|
| 342 | if (node instanceof Element) { |
|---|
| 343 | Element element = (Element) node; |
|---|
| 344 | if (element.getLocalName().equals("value")) { |
|---|
| 345 | names.append(name); |
|---|
| 346 | values.append("'"); |
|---|
| 347 | values.append(element.getValue()); |
|---|
| 348 | values.append("'"); |
|---|
| 349 | } else if (element.getLocalName().equals("autovalue")) { |
|---|
| 350 | names.append(name); |
|---|
| 351 | values.append(getAsPrimaryKey(id)); |
|---|
| 352 | } else if (element.getLocalName().equals("ref")) { |
|---|
| 353 | String reference = element.getValue(); |
|---|
| 354 | if (reference.length() > 0) { |
|---|
| 355 | if (items.containsKey(reference)) { |
|---|
| 356 | names.append(name); |
|---|
| 357 | values.append(getAsPrimaryKey(element.getValue())); |
|---|
| 358 | } else { |
|---|
| 359 | logger.severe("ERROR! the reference " + reference |
|---|
| 360 | + " could not be resolved."); |
|---|
| 361 | valid = false; |
|---|
| 362 | } |
|---|
| 363 | } |
|---|
| 364 | } |
|---|
| 365 | } |
|---|
| 366 | if (position == end) { |
|---|
| 367 | names.append(") "); |
|---|
| 368 | values.append(") "); |
|---|
| 369 | } else { |
|---|
| 370 | names.append(", "); |
|---|
| 371 | values.append(", "); |
|---|
| 372 | } |
|---|
| 373 | } |
|---|
| 374 | if (attributes.size() > 0) { |
|---|
| 375 | query.append("INSERT INTO " + tableName); |
|---|
| 376 | query.append(" "); |
|---|
| 377 | query.append(names); |
|---|
| 378 | query.append(" VALUES "); |
|---|
| 379 | query.append(values); |
|---|
| 380 | } |
|---|
| 381 | logger.fine(query.toString()); |
|---|
| 382 | if (disableConstraints) { |
|---|
| 383 | preQueries.addAll(buildEnableConstraints(false, tableName, |
|---|
| 384 | hibernateProperties)); |
|---|
| 385 | insertQueries.add(query.toString()); |
|---|
| 386 | postQueries.addAll(buildEnableConstraints(true, tableName, |
|---|
| 387 | hibernateProperties)); |
|---|
| 388 | } else { |
|---|
| 389 | insertQueries.add(query.toString()); |
|---|
| 390 | } |
|---|
| 391 | } |
|---|
| 392 | queries.addAll(preQueries); |
|---|
| 393 | queries.addAll(insertQueries); |
|---|
| 394 | return valid; |
|---|
| 395 | } |
|---|
| 396 | |
|---|
| 397 | /** |
|---|
| 398 | * Reorder the items. |
|---|
| 399 | * |
|---|
| 400 | * @param items the document items. |
|---|
| 401 | * @param dependencyMap the dependency map |
|---|
| 402 | * @param result the resulting list of id's |
|---|
| 403 | * @param leftover the leftover items if any. |
|---|
| 404 | */ |
|---|
| 405 | private void reOrderItems(final Map<String, Document> items, |
|---|
| 406 | final Map<String, Set<String>> dependencyMap, final List<String> result, |
|---|
| 407 | final List<String> leftover) { |
|---|
| 408 | |
|---|
| 409 | boolean changed; |
|---|
| 410 | logger.fine("Reordering " + items.size() + " items ..."); |
|---|
| 411 | final Map<String, Document> itemPool = new HashMap<String, Document>(items); |
|---|
| 412 | do { |
|---|
| 413 | changed = false; |
|---|
| 414 | |
|---|
| 415 | Iterator<Map.Entry<String, Document>> entries = itemPool.entrySet() |
|---|
| 416 | .iterator(); |
|---|
| 417 | while (entries.hasNext()) { |
|---|
| 418 | Map.Entry<String, Document> item = entries.next(); |
|---|
| 419 | String id = item.getKey(); |
|---|
| 420 | boolean accepted = true; |
|---|
| 421 | |
|---|
| 422 | Set<String> dependencies = dependencyMap.get(id); |
|---|
| 423 | for (String dependency : dependencies) { |
|---|
| 424 | if (!result.contains(dependency)) { |
|---|
| 425 | accepted = false; |
|---|
| 426 | break; |
|---|
| 427 | } |
|---|
| 428 | } |
|---|
| 429 | if (accepted) { |
|---|
| 430 | List<String> declaredDependencies = getDeclaredDependencies(item |
|---|
| 431 | .getValue()); |
|---|
| 432 | for (String declaredDependency : declaredDependencies) { |
|---|
| 433 | if (!result.contains(declaredDependency)) { |
|---|
| 434 | accepted = false; |
|---|
| 435 | break; |
|---|
| 436 | } |
|---|
| 437 | } |
|---|
| 438 | } |
|---|
| 439 | |
|---|
| 440 | if (accepted) { |
|---|
| 441 | logger.fine("Accepting item: " + id); |
|---|
| 442 | entries.remove(); |
|---|
| 443 | result.add(id); |
|---|
| 444 | changed = true; |
|---|
| 445 | } |
|---|
| 446 | } |
|---|
| 447 | } while (changed); |
|---|
| 448 | logger.fine("Accepted " + result.size() + " items ..."); |
|---|
| 449 | |
|---|
| 450 | if (!itemPool.isEmpty()) { |
|---|
| 451 | logger |
|---|
| 452 | .warning("Reference loop(s) detected! The following items will be added ignoring db-constraints:"); |
|---|
| 453 | for (String id : itemPool.keySet()) { |
|---|
| 454 | leftover.add(id); |
|---|
| 455 | StringBuffer dependencyList = new StringBuffer(); |
|---|
| 456 | Set<String> dependencies = dependencyMap.get(id); |
|---|
| 457 | for (String dependency : dependencies) { |
|---|
| 458 | dependencyList.append(dependency); |
|---|
| 459 | dependencyList.append(" "); |
|---|
| 460 | } |
|---|
| 461 | |
|---|
| 462 | logger.warning(id + ": [" + dependencyList.toString() + "]"); |
|---|
| 463 | } |
|---|
| 464 | } |
|---|
| 465 | } |
|---|
| 466 | |
|---|
| 467 | /** |
|---|
| 468 | * Validate dependencies. |
|---|
| 469 | * |
|---|
| 470 | * @param items the items |
|---|
| 471 | * @param dependencyMap the dependency map |
|---|
| 472 | * @return true, if successful |
|---|
| 473 | */ |
|---|
| 474 | private boolean validateDependencies(Map<String, Document> items, |
|---|
| 475 | Map<String, Set<String>> dependencyMap) { |
|---|
| 476 | |
|---|
| 477 | boolean valid = true; |
|---|
| 478 | |
|---|
| 479 | for (Map.Entry<String, Set<String>> dependenciesEntry : dependencyMap.entrySet()) { |
|---|
| 480 | |
|---|
| 481 | String id = dependenciesEntry.getKey(); |
|---|
| 482 | Set<String> dependencies = dependenciesEntry.getValue(); |
|---|
| 483 | for (String dependency : dependencies) { |
|---|
| 484 | if (!items.containsKey(dependency)) { |
|---|
| 485 | logger.severe("Item " + id + " contains invalid reference: " |
|---|
| 486 | + dependency + " (Note: Self references are not allowed!)"); |
|---|
| 487 | valid = false; |
|---|
| 488 | } |
|---|
| 489 | } |
|---|
| 490 | } |
|---|
| 491 | return valid; |
|---|
| 492 | } |
|---|
| 493 | |
|---|
| 494 | /** |
|---|
| 495 | * Builds the dependency map. |
|---|
| 496 | * |
|---|
| 497 | * @param items the items to build from. |
|---|
| 498 | * @return the Map of dependencies. |
|---|
| 499 | */ |
|---|
| 500 | private Map<String, Set<String>> buildDependencyMap(final Map<String, Document> items) { |
|---|
| 501 | |
|---|
| 502 | Map<String, Set<String>> dependencyMap = new HashMap<String, Set<String>>(); |
|---|
| 503 | |
|---|
| 504 | for (Map.Entry<String, Document> item : items.entrySet()) { |
|---|
| 505 | |
|---|
| 506 | String id = item.getKey(); |
|---|
| 507 | Document document = item.getValue(); |
|---|
| 508 | |
|---|
| 509 | Set<String> dependencies = new HashSet<String>(); |
|---|
| 510 | |
|---|
| 511 | Nodes references = document.query("/td:item/td:attribute/td:ref", TD_CONTEXT); |
|---|
| 512 | |
|---|
| 513 | for (int j = 0; j < references.size(); j++) { |
|---|
| 514 | Node node = references.get(j); |
|---|
| 515 | String refId = node.getValue(); |
|---|
| 516 | if (refId.length() > 0) { |
|---|
| 517 | dependencies.add(refId); |
|---|
| 518 | } else { |
|---|
| 519 | // Drop empty references. |
|---|
| 520 | node.getParent().detach(); |
|---|
| 521 | } |
|---|
| 522 | } |
|---|
| 523 | |
|---|
| 524 | dependencyMap.put(id, dependencies); |
|---|
| 525 | } |
|---|
| 526 | return dependencyMap; |
|---|
| 527 | } |
|---|
| 528 | |
|---|
| 529 | /** |
|---|
| 530 | * Recursively scan the directory and add found items to the map. |
|---|
| 531 | * |
|---|
| 532 | * @param dirName the directory name |
|---|
| 533 | * @param itemMap the item map |
|---|
| 534 | */ |
|---|
| 535 | private boolean loadItems(String dirName, Map<String, Document> itemMap) { |
|---|
| 536 | |
|---|
| 537 | File rootDir = new File(dirName); |
|---|
| 538 | if (!rootDir.exists()) { |
|---|
| 539 | logger.severe("Root directory '" + dirName + "' not found."); |
|---|
| 540 | return false; |
|---|
| 541 | } |
|---|
| 542 | String[] entries = rootDir.list(); |
|---|
| 543 | boolean success = true; |
|---|
| 544 | |
|---|
| 545 | for (int i = 0; i < entries.length; i++) { |
|---|
| 546 | String entry = dirName + FILE_SEPARATOR + entries[i]; |
|---|
| 547 | File subFile = new File(entry); |
|---|
| 548 | if (subFile.isDirectory()) { |
|---|
| 549 | success &= loadItems(entry, itemMap); |
|---|
| 550 | } else { |
|---|
| 551 | if (subFile.getName().endsWith(SUFFIX_XML)) { |
|---|
| 552 | Builder parser = new Builder(false); |
|---|
| 553 | try { |
|---|
| 554 | Document raw = parser.build(entry); |
|---|
| 555 | Document document = XIncluder.resolve(raw); |
|---|
| 556 | String id = getItemId(document); |
|---|
| 557 | |
|---|
| 558 | if (itemMap.containsKey(id)) { |
|---|
| 559 | logger.severe("Duplicate id detected: " + id |
|---|
| 560 | + " - item will be skipped"); |
|---|
| 561 | success = false; |
|---|
| 562 | } else { |
|---|
| 563 | itemMap.put(id, document); |
|---|
| 564 | logger.fine("Adding item: " + id); |
|---|
| 565 | } |
|---|
| 566 | } catch (Exception e) { |
|---|
| 567 | logger.log(Level.SEVERE, "Error parsing file " + entry + ": ", e); |
|---|
| 568 | } |
|---|
| 569 | } |
|---|
| 570 | } |
|---|
| 571 | } |
|---|
| 572 | return success; |
|---|
| 573 | } |
|---|
| 574 | |
|---|
| 575 | /** |
|---|
| 576 | * Gets the supplied id as a primary key representation. |
|---|
| 577 | * |
|---|
| 578 | * @param id the id |
|---|
| 579 | * @return the primary key |
|---|
| 580 | */ |
|---|
| 581 | protected String getAsPrimaryKey(final String id) { |
|---|
| 582 | |
|---|
| 583 | return "" + (sequenceBase + Long.parseLong(id.substring(3))); |
|---|
| 584 | } |
|---|
| 585 | |
|---|
| 586 | /** |
|---|
| 587 | * Returns the table name for the given item. |
|---|
| 588 | * |
|---|
| 589 | * @param item the item |
|---|
| 590 | * @return the table name |
|---|
| 591 | */ |
|---|
| 592 | protected String getTableName(final Document item) { |
|---|
| 593 | |
|---|
| 594 | Node type = item.query("//td:item/td:type", TD_CONTEXT).get(0); |
|---|
| 595 | return tablePrefix + type.getValue().toUpperCase(); |
|---|
| 596 | } |
|---|
| 597 | |
|---|
| 598 | /** |
|---|
| 599 | * Gets the item id. |
|---|
| 600 | * |
|---|
| 601 | * @param document the document containing the item. |
|---|
| 602 | * @return the item id if any, null else. |
|---|
| 603 | */ |
|---|
| 604 | private String getItemType(final Document document) { |
|---|
| 605 | |
|---|
| 606 | String type = null; |
|---|
| 607 | |
|---|
| 608 | Nodes nodes = document.query("//td:item/td:type", TD_CONTEXT); |
|---|
| 609 | for (int j = 0; j < nodes.size(); j++) { |
|---|
| 610 | Node node = nodes.get(j); |
|---|
| 611 | type = node.getValue(); |
|---|
| 612 | break; |
|---|
| 613 | } |
|---|
| 614 | if (type == null) { |
|---|
| 615 | logger.severe(getItemId(document) + " contains no type element!"); |
|---|
| 616 | } |
|---|
| 617 | return type; |
|---|
| 618 | } |
|---|
| 619 | |
|---|
| 620 | /** |
|---|
| 621 | * Gets the item id. |
|---|
| 622 | * |
|---|
| 623 | * @param document the document containing the item. |
|---|
| 624 | * @return the item id if any, null else. |
|---|
| 625 | */ |
|---|
| 626 | private String getItemId(final Document document) { |
|---|
| 627 | |
|---|
| 628 | String id = null; |
|---|
| 629 | |
|---|
| 630 | Nodes nodes = document.query("//td:item/td:id", TD_CONTEXT); |
|---|
| 631 | for (int j = 0; j < nodes.size(); j++) { |
|---|
| 632 | Node node = nodes.get(j); |
|---|
| 633 | id = node.getValue(); |
|---|
| 634 | break; |
|---|
| 635 | } |
|---|
| 636 | return id; |
|---|
| 637 | } |
|---|
| 638 | |
|---|
| 639 | /** |
|---|
| 640 | * Return the item attributes. |
|---|
| 641 | * |
|---|
| 642 | * @param document the document containing the item. |
|---|
| 643 | * @return the attributes of this item. |
|---|
| 644 | */ |
|---|
| 645 | private Map<String, Node> getAttributes(final Document document) { |
|---|
| 646 | |
|---|
| 647 | Map<String, Node> attributeMap = new HashMap<String, Node>(); |
|---|
| 648 | |
|---|
| 649 | Nodes attributes = document.query("//td:item/td:attribute", TD_CONTEXT); |
|---|
| 650 | for (int j = 0; j < attributes.size(); j++) { |
|---|
| 651 | Node attribute = attributes.get(j); |
|---|
| 652 | String name = attribute.query("td:name", TD_CONTEXT).get(0).getValue(); |
|---|
| 653 | Nodes values = attribute.query("td:value|td:autovalue|td:ref", TD_CONTEXT); |
|---|
| 654 | if (values.size() > 0) { |
|---|
| 655 | attributeMap.put(name, values.get(0)); |
|---|
| 656 | } |
|---|
| 657 | } |
|---|
| 658 | return attributeMap; |
|---|
| 659 | } |
|---|
| 660 | |
|---|
| 661 | /** |
|---|
| 662 | * Return the explicit item dependencies. |
|---|
| 663 | * |
|---|
| 664 | * @param document the document containing the item. |
|---|
| 665 | * @return the explicit dependencies of this item. |
|---|
| 666 | */ |
|---|
| 667 | private List<String> getDeclaredDependencies(final Document document) { |
|---|
| 668 | |
|---|
| 669 | List<String> result = new ArrayList<String>(); |
|---|
| 670 | |
|---|
| 671 | Nodes dependencies = document.query("//td:item/td:dependency", TD_CONTEXT); |
|---|
| 672 | for (int j = 0; j < dependencies.size(); j++) { |
|---|
| 673 | Node dependency = dependencies.get(j); |
|---|
| 674 | result.add(dependency.getValue()); |
|---|
| 675 | } |
|---|
| 676 | return result; |
|---|
| 677 | } |
|---|
| 678 | |
|---|
| 679 | /** |
|---|
| 680 | * Enable database constraints. |
|---|
| 681 | * |
|---|
| 682 | * @param enabled the enabled flag |
|---|
| 683 | * @param tableName the table name |
|---|
| 684 | * @param hibernateProperties the hibernate properties |
|---|
| 685 | * @return the list of queries |
|---|
| 686 | */ |
|---|
| 687 | private List<String> buildEnableConstraints(final boolean enabled, |
|---|
| 688 | final String tableName, final Properties hibernateProperties) { |
|---|
| 689 | |
|---|
| 690 | final List<String> queries = new ArrayList<String>(); |
|---|
| 691 | List<String> constraints = getConstraints(tableName, hibernateProperties); |
|---|
| 692 | for (String constraint : constraints) { |
|---|
| 693 | |
|---|
| 694 | final String query; |
|---|
| 695 | if (enabled) { |
|---|
| 696 | query = "ALTER TABLE " + tableName + " ENABLE CONSTRAINT " + constraint; |
|---|
| 697 | } else { |
|---|
| 698 | query = "ALTER TABLE " + tableName + " DISABLE CONSTRAINT " + constraint; |
|---|
| 699 | } |
|---|
| 700 | queries.add(query); |
|---|
| 701 | } |
|---|
| 702 | return queries; |
|---|
| 703 | } |
|---|
| 704 | |
|---|
| 705 | private List<String> getConstraints(String tableName, Properties hibernateProperties) { |
|---|
| 706 | |
|---|
| 707 | final List<String> constraints = new ArrayList<String>(); |
|---|
| 708 | try { |
|---|
| 709 | Connection conn = getConnection(hibernateProperties); |
|---|
| 710 | |
|---|
| 711 | for (SQLWarning warn = conn.getWarnings(); warn != null; warn = warn |
|---|
| 712 | .getNextWarning()) { |
|---|
| 713 | logger.warning("SQL Warning:"); |
|---|
| 714 | logger.warning("State : " + warn.getSQLState()); |
|---|
| 715 | logger.warning("Message: " + warn.getMessage()); |
|---|
| 716 | logger.warning("Error : " + warn.getErrorCode()); |
|---|
| 717 | } |
|---|
| 718 | |
|---|
| 719 | String query = "select constraint_name from user_constraints where table_name ='" |
|---|
| 720 | + tableName + "' and constraint_type='R'"; |
|---|
| 721 | |
|---|
| 722 | logger.info(query); |
|---|
| 723 | Statement stmt = conn.createStatement(); |
|---|
| 724 | ResultSet rs = stmt.executeQuery(query); |
|---|
| 725 | while (rs.next()) { |
|---|
| 726 | String constraint = rs.getString(1); |
|---|
| 727 | constraints.add(constraint); |
|---|
| 728 | } |
|---|
| 729 | rs.close(); |
|---|
| 730 | stmt.close(); |
|---|
| 731 | } catch (SQLException e) { |
|---|
| 732 | |
|---|
| 733 | logger.severe("SQL Exception:"); |
|---|
| 734 | |
|---|
| 735 | while (e != null) { |
|---|
| 736 | logger.severe("State : " + e.getSQLState()); |
|---|
| 737 | logger.severe("Message: " + e.getMessage()); |
|---|
| 738 | logger.severe("Error : " + e.getErrorCode()); |
|---|
| 739 | |
|---|
| 740 | e = e.getNextException(); |
|---|
| 741 | } |
|---|
| 742 | } catch (Exception e) { |
|---|
| 743 | logger.log(Level.SEVERE, "Exception: ", e); |
|---|
| 744 | } |
|---|
| 745 | return constraints; |
|---|
| 746 | } |
|---|
| 747 | |
|---|
| 748 | /** |
|---|
| 749 | * Gets the database connection for the given properties. |
|---|
| 750 | * |
|---|
| 751 | * @param hibernateProperties the hibernate properties |
|---|
| 752 | * @return the connection |
|---|
| 753 | * @throws ClassNotFoundException the class not found exception |
|---|
| 754 | * @throws SQLException the SQL exception |
|---|
| 755 | */ |
|---|
| 756 | private Connection getConnection(final Properties hibernateProperties) |
|---|
| 757 | throws ClassNotFoundException, SQLException { |
|---|
| 758 | |
|---|
| 759 | if (connection == null) { |
|---|
| 760 | Class.forName(hibernateProperties |
|---|
| 761 | .getProperty("hibernate.connection.driver_class")); |
|---|
| 762 | |
|---|
| 763 | Properties info = new Properties(); |
|---|
| 764 | info.setProperty("user", hibernateProperties |
|---|
| 765 | .getProperty("hibernate.connection.username")); |
|---|
| 766 | info.setProperty("password", hibernateProperties |
|---|
| 767 | .getProperty("hibernate.connection.password")); |
|---|
| 768 | info.setProperty("useUnicode", "true"); |
|---|
| 769 | info.setProperty("characterEncoding", "UTF-8"); |
|---|
| 770 | |
|---|
| 771 | connection = DriverManager.getConnection(hibernateProperties |
|---|
| 772 | .getProperty("hibernate.connection.url"), info); |
|---|
| 773 | } |
|---|
| 774 | return connection; |
|---|
| 775 | } |
|---|
| 776 | } |
|---|