1 22 23 package org.xquark.mapper.storage; 24 25 import java.io.StringWriter ; 26 import java.sql.*; 27 import java.util.*; 28 29 import org.xquark.mapper.RepositoryConnection; 30 import org.xquark.mapper.RepositoryDocument; 31 import org.xquark.mapper.RepositoryException; 32 import org.xquark.mapper.dbms.*; 33 import org.xquark.mapper.mapping.MappingConstants; 34 import org.xquark.mapper.mapping.MappingFactory; 35 import org.xquark.mapper.mapping.MappingSetIterator; 36 import org.xquark.mapper.metadata.*; 37 import org.xquark.mapper.util.*; 38 import org.xquark.schema.validation.SchemaValidationContext; 39 import org.xquark.util.SAXConstants; 40 import org.xquark.xml.xdbc.*; 41 42 43 48 49 public class XMLCollectionImpl 50 implements _RepositoryCollection, RepositoryConstants, SAXConstants 51 { 52 private static final String RCSRevision = "$Revision: 1.1 $"; 53 private static final String RCSName = "$Name: $"; 54 protected _RepositoryConnection repConnection; 58 protected AbstractConnection connection; 59 private DestructionToken destructor; 60 private CollectionMetadata metadata; 61 62 63 protected JDBCBatcher batcher; 64 protected PreparedStatement removeStmt; 65 protected PreparedStatement removeStructStmt; 66 protected PreparedStatement removeExtraStmt; 67 protected ArrayList removeValuesStmts; 68 protected PreparedStatement renameStmt; 69 70 protected HashSet filers = new HashSet(); protected RepositoryReader cachedReader; private ArrayList removedDocDIDs; 74 75 private boolean readOnly = false; 76 77 83 84 public XMLCollectionImpl(CollectionMetadata metadata, 85 _RepositoryConnection connection, DestructionToken destructor) 86 { 87 repConnection = connection; 88 this.connection = connection.getConnection(); 89 this.destructor = destructor; 90 this.metadata = metadata; 91 MappingFactory factory = new MappingFactory( 92 metadata.getMapping(), 93 connection.getSchemaManager(), 94 connection 95 ); 96 } 97 98 public CollectionMetadata getMetadata() 99 { 100 return metadata; 101 } 102 103 107 public RepositoryReader getReader() throws XMLDBCException 108 { 109 RepositoryReader ret; 110 if (cachedReader == null) 111 ret = new NSPrefixRepositoryFilter(new DocumentProducer(this)); 112 else 113 { 114 ret = cachedReader; 115 cachedReader = null; 116 } 117 return ret; 118 } 119 120 121 124 public void releaseReader(RepositoryReader reader) 125 throws RepositoryException 126 { 127 if (cachedReader == null) 128 { 129 reader.unPlug(); 130 cachedReader = reader; 131 } 132 else 133 reader.close(); 134 } 135 136 140 public void close() throws XMLDBCException 141 { 142 if (destructor != null) 144 { 145 try 146 { 147 destructor.destruct(this); 148 } 149 catch (RepositoryRuntimeException e) 150 { 151 throw e.getRepositoryException(); 152 } 153 destructor = null; 154 155 repConnection = null; 156 connection = null; 157 158 metadata = null; 159 160 closeFilers(); 161 filers = null; 162 163 if (cachedReader != null) 164 cachedReader.close(); 165 166 if (batcher != null) try 168 { 169 batcher.close(); 170 } 171 catch (SQLException e) 172 { 173 throw new RepositoryException(RepositoryException.DB_ERROR, 174 "Could not close the JDBC Batcher"); 175 } 176 try 177 { 178 if (removeStmt != null) 179 { 180 removeStmt.close(); 181 removeStmt = null; 182 } 183 if (removeStructStmt != null) 184 { 185 removeStructStmt.close(); 186 removeStructStmt = null; 187 } 188 if (removeExtraStmt != null) 189 { 190 removeExtraStmt.close(); 191 removeExtraStmt = null; 192 } 193 if (renameStmt != null) 194 { 195 renameStmt.close(); 196 renameStmt = null; 197 } 198 if (removeValuesStmts != null) 199 { 200 int len = removeValuesStmts.size(); 201 for (int i = 0; i < len; i++) 202 ((PreparedStatement)removeValuesStmts.get(i)).close(); 203 removeValuesStmts = null; 204 } 205 } 206 catch (SQLException e) 207 { 208 } 210 } 211 } 212 213 public _RepositoryConnection getRepositoryConnection() 214 { 215 return repConnection; 216 } 217 218 public class HandlerDestructor implements DestructionToken 219 { 220 public void destruct(Object o) 221 { 222 filers.remove(o); 223 } 224 } 225 226 public long getDocumentID(String ID) 227 throws SQLException, RepositoryException 228 { 229 Statement statement = null; 230 long docOID = -1; 231 try 232 { 233 statement = connection.getConnection().createStatement(); 234 ResultSet rs = statement.executeQuery( 235 metadata.getSelectDocIDStmt() + ID + "'"); 236 if(rs.next()) 237 docOID = rs.getLong(1); 238 rs.close(); 239 } 240 finally 241 { 242 if (statement != null) 243 statement.close(); 244 } 245 return docOID; 246 } 247 248 public CollectionInfo getInfo() 249 { 250 return metadata.getInfo(); 251 } 252 253 public String getCollectionName() 254 { 255 return metadata.getCollectionName(); 256 } 257 258 public String toString() 259 { 260 StringWriter out = new StringWriter (); 261 Tabulator tabulator = new Tabulator 262 ( 263 out, 264 79, 265 new int[] {0, 40} 266 ); 267 268 269 tabulator.addItem("name"); 270 tabulator.addItem(getCollectionName()); 271 272 String [] properties = getPropertyList(); 273 for (int i = 0; i < properties.length; i++) 274 { 275 tabulator.addItem(properties[i].substring(properties[i].lastIndexOf('/') + 1)); 276 try 277 { 278 tabulator.addItem(getProperty(properties[i])); 279 } 280 catch (XMLDBCNotRecognizedException e) 281 { 282 } 284 } 285 286 String [] features = getFeatureList(); 287 for (int i = 0; i < features.length; i++) 288 { 289 tabulator.addItem(features[i].substring(features[i].lastIndexOf('/') + 1)); 290 try 291 { 292 tabulator.addItem(String.valueOf(getFeature(features[i]))); 293 } 294 catch (XMLDBCNotRecognizedException e) 295 { 296 } 298 } 299 return out.toString(); 300 } 301 302 306 314 public XMLDocumentFiler getDocumentFiler() throws XMLDBCException 315 { 316 protectReadOnlyMethod(); 317 SchemaValidationContext validationContext = 318 new SchemaValidationContext(repConnection.getSchemaManager()); 319 ValidatingFilerAdapter validator = new ValidatingFilerAdapter(validationContext, repConnection); 320 validator.setDocumentBase("http://www.fake.com/"); 321 CollectionFiler handler = new CollectionFiler 322 ( 323 this, 324 validationContext, 325 !getFeature(RepositoryConnection.USE_SCHEMA_PREFIXES_FEATURE), 326 new XMLCollectionImpl.HandlerDestructor() 327 ); 328 validator.setSAXHandler(handler); 329 filers.add(handler); 330 return new XDBCFilerAdapter(validator, handler, handler, handler); 331 } 332 333 334 public XMLConnection getConnection() throws XMLDBCException 335 { 336 return getRepositoryConnection(); 337 } 338 339 public XMLDocument getDocument(String documentID) throws XMLDBCException 340 { 341 return getRepositoryDocument(documentID); 342 } 343 344 public boolean renameDocument(String oldID, String newID) throws XMLDBCException 345 { 346 return renameDocument(oldID, newID, true); 347 } 348 349 350 boolean renameDocument(String oldID, String newID, boolean useTransaction) throws XMLDBCException 351 { 352 protectReadOnlyMethod(); 353 boolean ret = false; 354 flushFilers(); 356 if (useTransaction) 358 connection.start(); 359 PreparedStatement statement = null; 360 try 361 { 362 statement = connection.getConnection().prepareStatement(metadata.getRenameDocStmt()); 363 statement.setString(1, newID); 364 statement.setString(2, oldID); 365 statement.executeUpdate(); 366 } 367 catch(SQLException e) 368 { 369 throw new RepositoryException(RepositoryException.DB_ERROR, "Relational database error while renaming a document in the document table.", e); 370 } 371 finally 372 { 373 try 374 { 375 if (statement != null) 376 statement.close(); 377 } 378 catch (SQLException e) 379 { 380 } 382 } 383 if (useTransaction && getConnection().getAutoCommit()) 384 { 385 connection.commit(); 386 commitPerformed(); 387 } 388 return ret; 389 } 390 391 public boolean removeDocument(String documentID) throws XMLDBCException 392 { 393 protectReadOnlyMethod(); 394 boolean ret = false; 395 flushFilers(); 397 ret = removeDocumentUsingBatch(documentID); 398 batcher.flush(); 399 if (getConnection().getAutoCommit()) 400 { 401 connection.commit(); 402 commitPerformed(); 403 } 404 return ret; 405 } 406 407 public boolean containsDocument(String documentID) throws XMLDBCException 408 { 409 boolean contains = false; 410 Statement statement = null; 412 try 413 { 414 statement = connection.getConnection().createStatement(); 415 ResultSet rs = statement.executeQuery(metadata.getContainsDocStmt() + documentID + "'"); 416 417 if (rs.next()&& (rs.getInt(1) > 0)) 418 contains = true; 419 420 rs.close(); 421 } 422 catch(SQLException e) 423 { 424 throw new RepositoryException(RepositoryException.DB_ERROR, "Relational database error while checking for document existence.", e); 425 } 426 finally 427 { 428 try 429 { 430 if (statement != null) 431 statement.close(); 432 } 433 catch (SQLException e) 434 { 435 } 437 } 438 return contains; 439 } 440 441 442 public String getName() throws XMLDBCException 443 { 444 return metadata.getCollectionName(); 445 } 446 447 public void setDescription(String description) throws XMLDBCException 448 { 449 metadata.updateDescription(description); 450 } 451 452 public String getDescription() throws XMLDBCException 453 { 454 return getInfo().getDescription(); 455 } 456 457 public long getDocumentCount() throws XMLDBCException 458 { 459 long numDocs = 0; 461 Statement statement = null; 462 463 try 464 { 465 statement = connection.getConnection().createStatement(); 466 ResultSet rs = statement.executeQuery(metadata.getCountDocStmt()); 467 468 if (rs.next()) 469 numDocs = rs.getLong(1); 470 rs.close(); 471 } 472 catch(SQLException e) 473 { 474 throw new RepositoryException(RepositoryException.DB_ERROR, "Relational database error while retrieving document list.", e); 475 } 476 finally 477 { 478 try 479 { 480 if (statement != null) 481 statement.close(); 482 } 483 catch (SQLException e) 484 { 485 } 487 } 488 return numDocs; 489 } 490 491 public List getIdentifierList() throws XMLDBCException 492 { 493 ArrayList list = new ArrayList(); 494 495 Statement statement = null; 497 try 498 { 499 statement = connection.getConnection().createStatement(); 500 ResultSet rs = statement.executeQuery(metadata.getDocListStmt()); 501 502 while (rs.next()) 503 { 504 list.add(rs.getString(1)); 505 } 506 rs.close(); 507 } 508 catch(SQLException e) 509 { 510 throw new RepositoryException(RepositoryException.DB_ERROR, "Relational database error while retrieving document list.", e); 511 } 512 finally 513 { 514 try 515 { 516 if (statement != null) 517 statement.close(); 518 } 519 catch (SQLException e) 520 { 521 } 523 } 524 return list; 525 } 526 527 public int removeAllDocuments() throws XMLDBCException 528 { 529 protectReadOnlyMethod(); 530 flushFilers(); 532 Iterator it = getIdentifierList().iterator(); 533 int docCount = 0; 534 535 while (it.hasNext()) 536 { 537 removeDocumentUsingBatch((String )it.next()); 538 docCount++; 539 if (getConnection().getAutoCommit()) 540 { 541 batcher.flush(); 542 connection.commit(); 543 commitPerformed(); 544 } 545 } 546 if (batcher != null) 547 batcher.flush(); 548 return docCount; 549 } 550 551 public void setReadOnly(boolean readOnly) throws XMLDBCException 552 { 553 if (readOnly) 554 closeFilers(); 555 this.readOnly = readOnly; 556 } 557 558 public boolean isReadOnly() throws XMLDBCException 559 { 560 return readOnly; 561 } 562 563 public List getRepositoryDocumentList() throws XMLDBCException 567 { 568 return metadata.getDocumentInfoList(this); 569 } 570 571 public RepositoryDocument getRepositoryDocument(String documentID) throws XMLDBCException 572 { 573 return new RepositoryDocumentImpl(documentID, this); 574 } 575 576 public void createIndexes() throws RepositoryException 577 { 578 protectReadOnlyMethod(); 579 metadata.createIndexes(); 580 } 581 582 public void dropIndexes() throws RepositoryException 583 { 584 protectReadOnlyMethod(); 585 metadata.dropIndexes(); 586 } 587 588 public void updateStatistics() throws XMLDBCException 589 { 590 protectReadOnlyMethod(); 591 metadata.updateStatistics(); 592 } 593 594 public void flushFilers() throws XMLDBCException 598 { 599 Iterator it = filers.iterator(); 600 while (it.hasNext()) 601 { 602 ((SAXHandler)it.next()).flushBuffer(); 603 } 604 } 605 606 public void commitPerformed() throws XMLDBCException 607 { 608 if (removedDocDIDs != null) 609 metadata.getUOIDManager().freeDocOIDs(removedDocDIDs); 610 } 611 612 public void rollbackPerformed() throws XMLDBCException 613 { 614 if (removedDocDIDs != null) 615 removedDocDIDs.clear(); 616 } 617 618 public void restoreDID(long did) 619 { 620 if (removedDocDIDs == null) 621 removedDocDIDs = new ArrayList(); 622 removedDocDIDs.add(new Long (did)); 623 } 624 625 629 public void setProperty(String propertyId, Object value) throws XMLDBCNotRecognizedException, XMLDBCNotSupportedException 630 { 631 getInfo().setProperty(propertyId, value); 632 } 633 634 public Object getProperty(String propertyId) throws XMLDBCNotRecognizedException 635 { 636 return getInfo().getProperty(propertyId); 637 } 638 639 public String [] getPropertyList() 640 { 641 return getInfo().getPropertyList(); 642 } 643 644 public void setFeature(String featureId, boolean state) throws XMLDBCNotRecognizedException, XMLDBCNotSupportedException 645 { 646 getInfo().setFeature(featureId, state); 647 } 648 649 public boolean getFeature(String featureId) throws XMLDBCNotRecognizedException 650 { 651 return getInfo().getFeature(featureId); 652 } 653 654 public String [] getFeatureList() 655 { 656 return getInfo().getFeatureList(); 657 } 658 659 private boolean removeDocumentUsingBatch(String documentID) throws XMLDBCException 663 { 664 if (batcher == null) 665 batcher = new JDBCBatcher(connection, true); 666 667 long UDID; 668 if (documentID == null) 669 throw new RepositoryException(RepositoryException.ILLEGAL_EXPRESSION, "The ID is mandatory to remove document."); 670 documentID = documentID.trim(); 672 673 try 674 { 675 UDID = getDocumentID(documentID); 676 } 677 catch(SQLException e) 678 { 679 throw new RepositoryException(RepositoryException.DB_ERROR, "Relational database error while retrieving the internal numeric ID.", e); 680 } 681 if (UDID < 0) 682 return false; 683 684 long first = metadata.getUOIDManager().getDocMinUOID(UDID); 685 long last = metadata.getUOIDManager().getDocMaxUOID(UDID); 686 687 connection.start(); 689 try 690 { 691 if (removeStmt == null) 692 { 693 String tableName = metadata.getTableInfo(TableSpec.TYPE_DOCS).getName(); 694 removeStmt = connection.getConnection().prepareStatement(metadata.getDeleteDocIDStmt()); 695 batcher.addStatement(removeStmt, tableName, RepositoryProperties.getIntProperty(CONF_REMOVE_BATCHSIZE)); 696 } 697 removeStmt.setString(1, documentID); 698 batcher.addBatch(removeStmt); 699 700 if (removeStructStmt == null) 702 { 703 TableInfo table = metadata.getTableInfo(TableSpec.TYPE_STRUCT); 704 removeStructStmt = StructSaver.getDeleteStatement(connection, table); 705 batcher.addStatement(removeStructStmt, table.getName(), RepositoryProperties.getIntProperty(CONF_REMOVE_BATCHSIZE)); 706 } 707 removeStructStmt.setLong(1, first); 708 removeStructStmt.setLong(2, last); 709 batcher.addBatch(removeStructStmt); 710 711 if (removeExtraStmt == null) 713 { 714 TableInfo table = metadata.getTableInfo(TableSpec.TYPE_EXTRA); 715 removeExtraStmt = ExtraDataSaver.getDeleteStatement(connection, table); 716 batcher.addStatement(removeExtraStmt, table.getName(), RepositoryProperties.getIntProperty(CONF_REMOVE_BATCHSIZE)); 717 } 718 719 removeExtraStmt.setLong(1, first); 720 removeExtraStmt.setLong(2, last); 721 batcher.addBatch(removeExtraStmt); 722 723 removeValues(first, last); 725 726 restoreDID(metadata.getUOIDManager().getDID(UDID)); 729 730 } 731 catch(Exception e) 732 { 733 try 734 { 735 if (getConnection().getAutoCommit()) 736 connection.rollback(); 737 rollbackPerformed(); } 739 catch(SQLException sqle) 740 { 741 } 743 if (e instanceof RepositoryException) 744 throw (RepositoryException)e; 745 else 746 throw new RepositoryException(RepositoryException.DB_ERROR, "Relational database error while deleting document rows.", e); 747 } 748 return true; 749 } 750 751 private void removeValues(long first, long last) 752 throws SQLException, RepositoryException 753 { 754 PreparedStatement stmt = null; 755 756 757 if (removeValuesStmts == null) 758 { 759 removeValuesStmts = new ArrayList(); 760 765 MappingSetIterator it = getMetadata().getSortedTableMappingSetIterator(true); 766 CollectionMappingInfo table; 767 768 while (it.hasNext()) 769 { 770 table = (CollectionMappingInfo)it.next(); 771 String SQLString = null; 772 773 if (it.isTableFirstOccurence()) 774 { 775 if (table.getTableMapping().getAction() == MappingConstants.INSERT) 777 { 778 if (table.getTableMapping().isClustered()) 779 SQLString = table.getDeleteStatement(); 780 else 781 SQLString = connection.getDeleteUserTableRowsStatement( 782 table.getTableMapping().getTableName(), 783 table.getDocumentRowsStatement() 784 ); 785 stmt = connection.getConnection().prepareStatement(SQLString); 786 removeValuesStmts.add(stmt); 787 batcher.addStatement(stmt,table.getTableMapping().getTableName(), 788 RepositoryProperties.getIntProperty(CONF_REMOVE_BATCHSIZE)); 789 } 790 791 SQLString = table.getOIDDeleteStatement(); 793 if (!table.getTableMapping().isClustered()) 794 { 795 stmt = connection.getConnection().prepareStatement(table.getOIDDeleteStatement()); 796 removeValuesStmts.add(stmt); 797 batcher.addStatement(stmt,table.getTableMapping().getTableName(), 798 RepositoryProperties.getIntProperty(CONF_REMOVE_BATCHSIZE)); 799 } 800 } 801 } 802 } 803 804 int len = removeValuesStmts.size(); 805 806 for(int i = 0; i < len; i++) 807 { 808 stmt = (PreparedStatement)removeValuesStmts.get(i); 809 stmt.setLong(1, first); 810 stmt.setLong(2, last); 811 batcher.addBatch(stmt); 812 } 813 } 814 815 protected void protectReadOnlyMethod() throws RepositoryException 816 { 817 if (readOnly) 818 throw new RepositoryException(RepositoryException.NOT_ALLOWED, 819 "Such operation is forbidden because collection is read-only."); 820 } 821 822 private void closeFilers() throws XMLDBCException 823 { 824 Iterator it = ((HashSet)filers.clone()).iterator(); 825 while(it.hasNext()) 826 { 827 ((SAXHandler)it.next()).close(); 828 it.remove(); 829 } 830 } 831 } | Popular Tags |