1 16 package org.outerj.daisy.repository.serverimpl; 17 18 import org.apache.xmlbeans.XmlObject; 19 import org.outerj.daisy.repository.commonimpl.*; 20 import org.outerj.daisy.repository.commonimpl.schema.RepositorySchemaImpl; 21 import org.outerj.daisy.repository.*; 22 import org.outerj.daisy.repository.serverimpl.linkextraction.LinkExtractorHelper; 23 import org.outerj.daisy.repository.serverimpl.linkextraction.LinkInfo; 24 import org.outerj.daisy.repository.serverimpl.model.LocalSchemaStrategy; 25 import org.outerj.daisy.repository.acl.AclResultInfo; 26 import org.outerj.daisy.repository.acl.AclPermission; 27 import org.outerj.daisy.repository.schema.FieldType; 28 import org.outerj.daisy.blobstore.NonExistingBlobException; 29 import org.outerj.daisy.blobstore.BlobIOException; 30 import org.outerj.daisy.jdbcutil.JdbcHelper; 31 import org.outerj.daisy.util.VersionHelper; 32 import org.outerx.daisy.x10.*; 33 34 35 import java.sql.*; 36 import java.util.*; 37 import java.util.Date ; 38 import java.math.BigDecimal ; 39 import java.io.InputStream ; 40 import java.io.IOException ; 41 42 import EDU.oswego.cs.dl.util.concurrent.Sync; 43 44 public class LocalDocumentStrategy extends AbstractLocalStrategy implements DocumentStrategy { 45 46 public LocalDocumentStrategy(LocalRepositoryManager.Context context, AuthenticatedUser systemUser, JdbcHelper jdbcHelper) { 47 super(context, systemUser, jdbcHelper); 48 } 49 50 public Document load(AuthenticatedUser user, long documentId, long branchId, long languageId) throws RepositoryException { 51 if (branchId == -1 || languageId == -1) 54 throw new RepositoryException("branchId and languageId parameters should not be -1"); 55 56 List executeAfterCommit = new ArrayList(2); 57 Connection conn = null; 58 try { 59 conn = context.getDataSource().getConnection(); 60 jdbcHelper.startTransaction(conn); 61 62 DocumentImpl document = loadDocumentInTransaction(user, documentId, branchId, languageId, conn, executeAfterCommit); 63 64 conn.commit(); 65 executeRunnables(executeAfterCommit); 66 67 AclResultInfo aclInfo = context.getCommonRepository().getAccessManager().getAclInfoOnLive(systemUser, 68 user.getId(), user.getActiveRoleIds(), document); 69 if (!aclInfo.isAllowed(AclPermission.READ)) { 70 if (aclInfo.isAllowed(AclPermission.READ_LIVE)) { 71 return new ReadLiveOnlyDocument(document, this); 72 } else { 73 throw new DocumentReadDeniedException(documentId, user.getLogin(), user.getId()); 74 } 75 } 76 77 return document; 78 } catch (Throwable e) { 79 jdbcHelper.rollback(conn); 80 if (e instanceof DocumentNotFoundException || e instanceof AccessException || e instanceof DocumentVariantNotFoundException) 81 throw (RepositoryException)e; 82 else 83 throw new RepositoryException("Error loading document " + documentId + ".", e); 84 } finally { 85 jdbcHelper.closeConnection(conn); 86 } 87 } 88 89 96 private DocumentImpl loadDocumentInTransaction(AuthenticatedUser user, long documentId, long branchId, long languageId, Connection conn, List executeAfterCommit) throws RepositoryException { 97 PreparedStatement stmt = null; 98 try { 99 stmt = conn.prepareStatement("select created, last_modified, last_modifier, owner, private, updatecount from documents where id = ?"); 100 stmt.setLong(1, documentId); 101 ResultSet rs = stmt.executeQuery(); 102 if (!rs.next()) 103 throw new DocumentNotFoundException(documentId); 104 105 DocumentImpl document = new DocumentImpl(this, context.getCommonRepository(), user, -1, branchId, languageId); 106 DocumentImpl.IntimateAccess documentInt = document.getIntimateAccess(this); 107 documentInt.load(documentId, rs.getTimestamp("last_modified"), rs.getLong("last_modifier"), 108 rs.getTimestamp("created"), rs.getLong("owner"), rs.getBoolean("private"), rs.getLong("updatecount")); 109 rs.close(); 110 stmt.close(); 111 112 if (branchId != -1 && languageId != -1) 113 loadVariant(document, documentInt, conn, executeAfterCommit); 114 115 return document; 116 } catch (DocumentNotFoundException e) { 117 throw e; 118 } catch (DocumentVariantNotFoundException e) { 119 throw e; 120 } catch (Throwable e) { 121 throw new RepositoryException("Error loading document.", e); 122 } finally { 123 jdbcHelper.closeStatement(stmt); 124 } 125 } 126 127 private void loadVariant(DocumentImpl document, DocumentImpl.IntimateAccess documentInt, Connection conn, List executeAfterCommit) throws RepositoryException, SQLException { 128 DocumentVariantImpl variant = documentInt.getVariant(); 129 DocumentVariantImpl.IntimateAccess variantInt = variant.getIntimateAccess(this); 130 PreparedStatement stmt = null; 131 try { 132 stmt = conn.prepareStatement("select doctype_id, retired, lastversion_id, liveversion_id, last_modified, last_modifier, updatecount, created_from_branch_id, created_from_lang_id, created_from_version_id from document_variants where doc_id = ? and branch_id = ? and lang_id = ?"); 133 stmt.setLong(1, document.getId()); 134 stmt.setLong(2, variant.getBranchId()); 135 stmt.setLong(3, variant.getLanguageId()); 136 ResultSet rs = stmt.executeQuery(); 137 138 if (!rs.next()) 139 throw new DocumentVariantNotFoundException(document.getId(), getBranchLabel(variant.getBranchId()), getLanguageLabel(variant.getLanguageId())); 140 141 variantInt.load(rs.getLong("doctype_id"), 142 rs.getBoolean("retired"), 143 rs.getLong("lastversion_id"), 144 rs.getLong("liveversion_id"), 145 rs.getTimestamp("last_modified"), 146 rs.getLong("last_modifier"), 147 rs.getLong("created_from_branch_id"), 148 rs.getLong("created_from_lang_id"), 149 rs.getLong("created_from_version_id"), 150 rs.getLong("updatecount")); 151 152 loadName(variant, variantInt, conn); 153 loadCustomFields(variant, variantInt, conn); 154 loadParts(variant, variantInt, conn); 155 loadFields(variant, variantInt, conn); 156 loadLinks(variant, variantInt, conn); 157 loadCollections(variant, variantInt, conn); 158 loadSummary(variant, variantInt, conn); 159 160 LockInfoImpl lockInfo = loadLock(document.getId(), document.getBranchId(), document.getLanguageId(), conn, false, executeAfterCommit); 161 variantInt.setLockInfo(lockInfo); 162 } finally { 163 jdbcHelper.closeStatement(stmt); 164 } 165 } 166 167 private void loadCollections(DocumentVariantImpl variant, DocumentVariantImpl.IntimateAccess variantInt, Connection conn) throws RepositoryException { 168 Collection collections = loadCollections(variant, conn); 169 if (collections != null) { 170 for (Iterator iter = collections.iterator(); iter.hasNext();) { 171 DocumentCollectionImpl element = (DocumentCollectionImpl) iter.next(); 172 variantInt.addCollection(element); 173 } 174 } 175 } 176 177 private void loadName(DocumentVariantImpl variant, DocumentVariantImpl.IntimateAccess variantInt, Connection conn) throws SQLException, RepositoryException { 178 PreparedStatement stmt = null; 179 try { 180 stmt = conn.prepareStatement("select \"name\" from document_versions where doc_id = ? and branch_id = ? and lang_id = ? and id = ?"); 181 stmt.setLong(1, variant.getDocumentId()); 182 stmt.setLong(2, variant.getBranchId()); 183 stmt.setLong(3, variant.getLanguageId()); 184 stmt.setLong(4, variantInt.getLastVersionId()); 185 ResultSet rs = stmt.executeQuery(); 186 if (!rs.next()) 187 throw new RepositoryException("Strange: no version record found for document ID " + variant.getDocumentId() + ", branch " + getBranchLabel(variant.getBranchId()) + ", language " + getLanguageLabel(variant.getLanguageId()) + ", version ID " + variantInt.getLastVersionId()); 188 variantInt.setName(rs.getString(1)); 189 } finally { 190 jdbcHelper.closeStatement(stmt); 191 } 192 } 193 194 private void loadCustomFields(DocumentVariantImpl variant, DocumentVariantImpl.IntimateAccess variantInt, Connection conn) throws SQLException { 195 PreparedStatement stmt = null; 196 try { 197 stmt = conn.prepareStatement("select \"name\", \"value\" from customfields where doc_id = ? and branch_id = ? and lang_id = ?"); 198 stmt.setLong(1, variant.getDocumentId()); 199 stmt.setLong(2, variant.getBranchId()); 200 stmt.setLong(3, variant.getLanguageId()); 201 ResultSet rs = stmt.executeQuery(); 202 while (rs.next()) { 203 variantInt.setCustomField(rs.getString(1), rs.getString(2)); 204 } 205 } finally { 206 jdbcHelper.closeStatement(stmt); 207 } 208 } 209 210 private void loadParts(DocumentVariantImpl variant, DocumentVariantImpl.IntimateAccess variantInt, Connection conn) throws SQLException { 211 java.util.Collection parts = loadParts(variant, variantInt, variantInt.getLastVersionId(), conn); 212 Iterator partsIt = parts.iterator(); 213 while (partsIt.hasNext()) { 214 PartImpl part = (PartImpl)partsIt.next(); 215 variantInt.addPart(part); 216 } 217 } 218 219 private Collection loadParts(DocumentVariantImpl variant, DocumentVariantImpl.IntimateAccess variantInt, 220 long versionId, Connection conn) throws SQLException { 221 PreparedStatement stmt = null; 222 try { 223 stmt = conn.prepareStatement("select blob_id, mimetype, filename, parttype_id, blob_size from parts where doc_id = ? and branch_id = ? and lang_id = ? and version_id = ?"); 224 stmt.setLong(1, variant.getDocumentId()); 225 stmt.setLong(2, variant.getBranchId()); 226 stmt.setLong(3, variant.getLanguageId()); 227 stmt.setLong(4, versionId); 228 ResultSet rs = stmt.executeQuery(); 229 230 ArrayList parts = new ArrayList(5); 231 while (rs.next()) { 232 PartImpl part = new PartImpl(variantInt, rs.getLong("parttype_id"), versionId); 233 PartImpl.IntimateAccess partInt = part.getIntimateAccess(this); 234 partInt.setBlobKey(rs.getString("blob_id")); 235 partInt.setMimeType(rs.getString("mimetype")); 236 partInt.setFileName(rs.getString("filename")); 237 partInt.setSize(rs.getLong("blob_size")); 238 parts.add(part); 239 } 240 return parts; 241 } finally { 242 jdbcHelper.closeStatement(stmt); 243 } 244 } 245 246 private void loadFields(DocumentVariantImpl variant, DocumentVariantImpl.IntimateAccess variantInt, Connection conn) throws SQLException { 247 Collection fields = loadFields(variant, variantInt.getLastVersionId(), conn); 248 Iterator fieldsIt = fields.iterator(); 249 while (fieldsIt.hasNext()) { 250 variantInt.addField((FieldImpl)fieldsIt.next()); 251 } 252 } 253 254 private Collection loadFields(DocumentVariantImpl variant, long versionId, Connection conn) throws SQLException { 255 PreparedStatement stmt = null; 256 try { 257 stmt = conn.prepareStatement("select fieldtype_id, stringvalue, datevalue, datetimevalue, integervalue, floatvalue, decimalvalue, booleanvalue, link_docid, link_branchid, link_langid from thefields where doc_id = ? and branch_id = ? and lang_id = ? and version_id = ? order by fieldtype_id, value_seq"); 258 stmt.setLong(1, variant.getDocumentId()); 259 stmt.setLong(2, variant.getBranchId()); 260 stmt.setLong(3, variant.getLanguageId()); 261 stmt.setLong(4, versionId); 262 ResultSet rs = stmt.executeQuery(); 263 264 ArrayList fields = new ArrayList(10); 265 List multiValueValues = null; 266 FieldType previousFieldType = null; 267 while (rs.next()) { 268 long fieldTypeId = rs.getLong("fieldtype_id"); 269 FieldType fieldType; 270 if (previousFieldType == null || previousFieldType.getId() != fieldTypeId) { 271 try { 272 fieldType = context.getRepositorySchema().getFieldTypeById(fieldTypeId, false, systemUser); 273 } catch (RepositoryException e) { 274 throw new RuntimeException (DocumentImpl.ERROR_ACCESSING_REPOSITORY_SCHEMA, e); 275 } 276 } else { 277 fieldType = previousFieldType; 278 } 279 280 if (previousFieldType != null && previousFieldType.getId() != fieldTypeId && multiValueValues != null) { 281 Object [] values = multiValueValues.toArray(); 282 FieldImpl field = new FieldImpl(variant.getIntimateAccess(this), previousFieldType.getId(), values); 283 fields.add(field); 284 multiValueValues = null; 285 } 286 287 ValueType valueType = fieldType.getValueType(); 288 LocalSchemaStrategy.FieldValueGetter valueGetter = LocalSchemaStrategy.getValueGetter(valueType); 289 Object value = valueGetter.getValue(rs); 290 if (fieldType.isMultiValue()) { 291 if (multiValueValues == null) 292 multiValueValues = new ArrayList(10); 293 multiValueValues.add(value); 294 } else { 295 FieldImpl field = new FieldImpl(variant.getIntimateAccess(this), rs.getLong(1), value); 296 fields.add(field); 297 } 298 299 previousFieldType = fieldType; 300 } 301 302 if (multiValueValues != null) { 303 Object [] values = multiValueValues.toArray(); 304 FieldImpl field = new FieldImpl(variant.getIntimateAccess(this), previousFieldType.getId(), values); 305 fields.add(field); 306 } 307 308 return fields; 309 } finally { 310 jdbcHelper.closeStatement(stmt); 311 } 312 } 313 314 private void loadLinks(DocumentVariantImpl variant, DocumentVariantImpl.IntimateAccess variantInt, Connection conn) throws SQLException { 315 Collection links = loadLinks(variant, variantInt.getLastVersionId(), conn); 316 Iterator linksIt = links.iterator(); 317 while (linksIt.hasNext()) { 318 variantInt.addLink((LinkImpl)linksIt.next()); 319 } 320 } 321 322 private Collection loadLinks(DocumentVariantImpl variant, long versionId, Connection conn) throws SQLException { 323 PreparedStatement stmt = null; 324 try { 325 stmt = conn.prepareStatement("select title, target from links where doc_id = ? and branch_id = ? and lang_id = ? and version_id = ? order by id"); 326 stmt.setLong(1, variant.getDocumentId()); 327 stmt.setLong(2, variant.getBranchId()); 328 stmt.setLong(3, variant.getLanguageId()); 329 stmt.setLong(4, versionId); 330 ResultSet rs = stmt.executeQuery(); 331 332 ArrayList links = new ArrayList(5); 333 while (rs.next()) { 334 LinkImpl link = new LinkImpl(rs.getString(1), rs.getString(2)); 335 links.add(link); 336 } 337 rs.close(); 338 339 return links; 340 } finally { 341 jdbcHelper.closeStatement(stmt); 342 } 343 } 344 345 private void loadSummary(DocumentVariantImpl variant, DocumentVariantImpl.IntimateAccess variantInt, Connection conn) throws SQLException { 346 PreparedStatement stmt = null; 347 try { 348 stmt = conn.prepareStatement("select summary from summaries where doc_id = ? and branch_id = ? and lang_id = ?"); 349 stmt.setLong(1, variant.getDocumentId()); 350 stmt.setLong(2, variant.getBranchId()); 351 stmt.setLong(3, variant.getLanguageId()); 352 ResultSet rs = stmt.executeQuery(); 353 String summary = null; 354 if (rs.next()) { 355 summary = rs.getString(1); 356 } 357 variantInt.setSummary(summary); 358 } finally { 359 jdbcHelper.closeStatement(stmt); 360 } 361 } 362 363 public void store(DocumentImpl document) throws RepositoryException { 364 DocumentImpl.IntimateAccess documentInt = document.getIntimateAccess(this); 365 DocumentVariantImpl variant = documentInt.getVariant(); 366 DocumentVariantImpl.IntimateAccess variantInt = variant.getIntimateAccess(this); 367 368 if (!variant.isNew()) { 370 AclResultInfo aclInfo = context.getCommonRepository().getAccessManager().getAclInfoOnLive(systemUser, 375 documentInt.getCurrentUser().getId(), documentInt.getCurrentUser().getActiveRoleIds(), 376 document.getId(), document.getBranchId(), document.getLanguageId()); 377 if (!aclInfo.isAllowed(AclPermission.WRITE)) 378 throw new AccessException("Write access denied for document " + document.getId() + ", branch " + getBranchLabel(document.getBranchId()) + ", language " + getLanguageLabel(document.getLanguageId()) + " for user " + documentInt.getCurrentUser().getLogin() + " (ID: " + documentInt.getCurrentUser().getId() + ")."); 379 } 380 381 AclResultInfo newAclInfo = context.getCommonRepository().getAccessManager().getAclInfoOnLive(systemUser, 383 documentInt.getCurrentUser().getId(), documentInt.getCurrentUser().getActiveRoleIds(), document); 384 385 if (!newAclInfo.isAllowed(AclPermission.READ)) 386 throw new RepositoryException("The document cannot be saved because the user would not have read access to it anymore."); 387 388 if (!newAclInfo.isAllowed(AclPermission.WRITE)) 389 throw new RepositoryException("The document cannot be saved because the user would not have write access to it anymore."); 390 391 List preSaveHooks = context.getPreSaveHooks(); 393 if (preSaveHooks.size() > 0) { 394 Iterator preSaveHookIt = preSaveHooks.iterator(); 395 while (preSaveHookIt.hasNext()) { 396 Repository userRepository = new RepositoryImpl(context.getCommonRepository(), documentInt.getCurrentUser()); 397 PreSaveHook hook = (PreSaveHook)preSaveHookIt.next(); 398 try { 399 hook.process(document, userRepository); 400 } catch (Throwable e) { 401 logger.error("Error executing pre-save-hook " + hook.getName() + " on document " + document.getVariantKey(), e); 402 } 403 } 404 } 405 406 boolean canPublish = newAclInfo.isAllowed(AclPermission.PUBLISH); 408 if (document.getNewVersionState() == VersionState.PUBLISH && !canPublish) 409 document.setNewVersionState(VersionState.DRAFT); 410 411 long id = document.getId(); 412 boolean isDocumentNew = (id == -1); 413 boolean isVariantNew = variant.isNew(); 414 List partUpdates = null; 415 416 List executeAfterCommit = new ArrayList(2); 418 Connection conn = null; 419 PreparedStatement stmt = null; 420 try { 421 conn = context.getDataSource().getConnection(); 422 jdbcHelper.startTransaction(conn); 423 424 DocumentImpl oldDocument = null; 427 if (!isDocumentNew && !variant.isNew()) { 428 oldDocument = loadDocumentInTransaction(documentInt.getCurrentUser(), id, variant.getBranchId(), variant.getLanguageId(), conn, executeAfterCommit); 429 } else if (!isDocumentNew && variant.isNew()) { 430 oldDocument = loadDocumentInTransaction(documentInt.getCurrentUser(), id, -1, -1, conn, executeAfterCommit); 431 } 432 433 Date now = new Date (); 434 435 439 long documentNewUpdateCount = document.getUpdateCount(); 442 443 if (id == -1) { 444 id = context.getNextDocumentId(); 445 documentNewUpdateCount = 1; 446 447 stmt = conn.prepareStatement("insert into documents(id, created, owner, private, last_modified, last_modifier, updatecount) values(?,?,?,?,?,?,?)"); 448 stmt.setLong(1, id); 449 stmt.setTimestamp(2, new Timestamp(now.getTime())); 450 stmt.setLong(3, document.getOwner()); 451 stmt.setBoolean(4, document.isPrivate()); 452 stmt.setTimestamp(5, new Timestamp(now.getTime())); 453 stmt.setLong(6, documentInt.getCurrentUser().getId()); stmt.setLong(7, documentNewUpdateCount); 455 456 stmt.execute(); 457 stmt.close(); 458 459 XmlObject eventDescription = createNewDocumentEvent(document, id, now, 1L); 461 eventHelper.createEvent(eventDescription, "DocumentCreated", conn); 462 } else if (document.needsSaving()) { 463 stmt = conn.prepareStatement("select updatecount from documents where id = ? " + jdbcHelper.getSharedLockClause()); 466 stmt.setLong(1, id); 467 ResultSet rs = stmt.executeQuery(); 468 if (!rs.next()) { 469 throw new RepositoryException("The document with ID " + id + " does not exist in the repository."); 470 } else { 471 long dbUpdateCount = rs.getLong(1); 476 if (dbUpdateCount != document.getUpdateCount()) 477 throw new RepositoryException("The document was updated concurrently."); 478 } 479 rs.close(); 480 stmt.close(); 481 482 documentNewUpdateCount = document.getUpdateCount() + 1; 483 484 stmt = conn.prepareStatement("update documents set last_modified = ?, last_modifier = ?, private = ?, owner = ?, updatecount = ? where id = ?"); 486 stmt.setTimestamp(1, new Timestamp(now.getTime())); 487 stmt.setLong(2, documentInt.getCurrentUser().getId()); 488 stmt.setBoolean(3, document.isPrivate()); 489 stmt.setLong(4, document.getOwner()); 490 stmt.setLong(5, documentNewUpdateCount); 491 stmt.setLong(6, id); 492 stmt.executeUpdate(); 493 stmt.close(); 494 495 XmlObject eventDescription = createDocumentUpdatedEvent(oldDocument, document, now, documentNewUpdateCount); 497 eventHelper.createEvent(eventDescription, "DocumentUpdated", conn); 498 499 conn.commit(); 502 executeRunnables(executeAfterCommit); 503 504 documentInt.saved(id, now, document.getCreated(), documentNewUpdateCount); 505 506 context.getCommonRepository().fireRepositoryEvent(RepositoryEventType.DOCUMENT_UPDATED, id, document.getUpdateCount()); 507 } 508 509 510 514 515 LockInfo lockInfo = loadLock(document.getId(), document.getBranchId(), document.getLanguageId(), conn, true, executeAfterCommit); 519 if (lockInfo.hasLock() && lockInfo.getType() == LockType.PESSIMISTIC 520 && lockInfo.getUserId() != documentInt.getCurrentUser().getId()) { 521 throw new RepositoryException("Cannot store document variant because someone else is holding a pessimistic lock on it: " + lockInfo.getUserId()); 522 } 523 524 boolean newVersion = false; 525 if (variant.isNew()) { 526 stmt = conn.prepareStatement("insert into document_variants(doc_id, branch_id, lang_id, doctype_id, retired, lastversion_id, liveversion_id, last_modified, last_modifier, updatecount, created_from_branch_id, created_from_lang_id, created_from_version_id) values(?,?,?,?,?,?,?,?,?,?,?,?,?)"); 527 stmt.setLong(1, id); 528 stmt.setLong(2, document.getBranchId()); 529 stmt.setLong(3, document.getLanguageId()); 530 stmt.setLong(4, variant.getDocumentTypeId()); 531 stmt.setBoolean(5, variant.isRetired()); 532 stmt.setLong(6, -1); 533 stmt.setLong(7, -1); 534 stmt.setTimestamp(8, new Timestamp(now.getTime())); 535 stmt.setLong(9, documentInt.getCurrentUser().getId()); 536 stmt.setLong(10, 1L); 537 stmt.setLong(11, variant.getCreatedFromBranchId()); 538 stmt.setLong(12, variant.getCreatedFromLanguageId()); 539 stmt.setLong(13, variant.getCreatedFromVersionId()); 540 541 stmt.execute(); 542 stmt.close(); 543 newVersion = true; 544 } else if (variant.needsSaving()) { 545 stmt = conn.prepareStatement("select updatecount from document_variants where doc_id = ? and branch_id = ? and lang_id = ? " + jdbcHelper.getSharedLockClause()); 548 stmt.setLong(1, id); 549 stmt.setLong(2, document.getBranchId()); 550 stmt.setLong(3, document.getLanguageId()); 551 ResultSet rs = stmt.executeQuery(); 552 if (!rs.next()) { 553 throw new RepositoryException("The document variant for document ID " + id + ", branch ID " + document.getBranchId() + ", language ID " + document.getLanguageId() + " does not exist in the repository."); 554 } else { 555 long dbUpdateCount = rs.getLong(1); 556 if (dbUpdateCount != variant.getUpdateCount()) 557 throw new RepositoryException("The document variant was updated concurrently."); 558 } 559 rs.close(); 560 stmt.close(); 561 newVersion = true; 562 } 563 564 long lastVersionId = -1; 565 long liveVersionId = -1; 566 boolean summaryNeedsUpdating = false; 567 String summary = null; 568 long variantNewUpdateCount = -1; 569 if (newVersion) { 570 571 if (variantInt.hasCustomFieldChanges()) 573 storeCustomFields(document, id, conn); 574 575 if (variantInt.hasCollectionChanges()) 576 storeCollections(variant, id, conn); 577 578 579 if (variant.needsNewVersion()) { 580 lastVersionId = createVersion(document, variantInt, id, now, conn); 581 partUpdates = storeParts(variant, variantInt, id, lastVersionId, conn); 582 storeFields(document, id, lastVersionId, conn); 583 storeLinks(document, id, lastVersionId, conn); 584 if (document.getNewVersionState() == VersionState.PUBLISH) 585 summaryNeedsUpdating = true; 586 } else { 587 lastVersionId = variantInt.getLastVersionId(); 588 } 589 590 liveVersionId = getLastPublishedVersionId(conn, id, document.getBranchId(), document.getLanguageId()); 591 592 variantNewUpdateCount = variant.getUpdateCount() + 1; 593 594 stmt = conn.prepareStatement("update document_variants set last_modified = ?, last_modifier = ?, lastversion_id = ?, liveversion_id = ?, retired = ?, updatecount = ?, doctype_id = ? where doc_id = ? and branch_id = ? and lang_id = ?"); 596 stmt.setTimestamp(1, new Timestamp(now.getTime())); 597 stmt.setLong(2, documentInt.getCurrentUser().getId()); 598 stmt.setLong(3, lastVersionId); 599 stmt.setLong(4, liveVersionId); 600 stmt.setBoolean(5, document.isRetired()); 601 stmt.setLong(6, variantNewUpdateCount); 602 stmt.setLong(7, document.getDocumentTypeId()); 603 stmt.setLong(8, id); 604 stmt.setLong(9, document.getBranchId()); 605 stmt.setLong(10, document.getLanguageId()); 606 stmt.executeUpdate(); 607 stmt.close(); 608 609 LinkExtractorHelper linkExtractorHelper = new LinkExtractorHelper(document, id, liveVersionId, lastVersionId, true, systemUser, context); 611 Collection links = linkExtractorHelper.extract(); 612 storeExtractedLinks(conn, id, document.getBranchId(), document.getLanguageId(), links); 613 614 if (summaryNeedsUpdating) { 616 summary = storeSummary(conn, document, id, -1); 617 } else { 618 summary = document.getSummary(); 619 } 620 621 622 XmlObject eventDescription; 626 if (isVariantNew) { 627 eventDescription = createNewVariantEvent(document, id, now, summary, variantNewUpdateCount, documentNewUpdateCount); 628 } else { 629 eventDescription = createVariantUpdatedEvent(oldDocument, document, now, summary, lastVersionId, variantNewUpdateCount, documentNewUpdateCount); 630 } 631 632 eventHelper.createEvent(eventDescription, isVariantNew ? "DocumentVariantCreated" : "DocumentVariantUpdated", conn); 633 } 634 635 conn.commit(); 636 executeRunnables(executeAfterCommit); 637 638 if (newVersion) 640 variantInt.saved(lastVersionId, liveVersionId, now, summary, variantNewUpdateCount); 641 if (isDocumentNew) 642 documentInt.saved(id, now, now, documentNewUpdateCount); 643 } catch (Throwable e) { 644 jdbcHelper.rollback(conn); 645 if (partUpdates != null) { 646 for (Iterator partUpdatesIt = partUpdates.iterator(); partUpdatesIt.hasNext();) { 647 PartUpdate partUpdate = (PartUpdate)partUpdatesIt.next(); 648 partUpdate.rollback(); 649 } 650 } 651 throw new RepositoryException("Problem storing document.", e); 652 } finally { 653 jdbcHelper.closeStatement(stmt); 654 jdbcHelper.closeConnection(conn); 655 } 656 657 if (isDocumentNew) 659 context.getCommonRepository().fireRepositoryEvent(RepositoryEventType.DOCUMENT_CREATED, id, document.getUpdateCount()); 660 661 if (isVariantNew) 662 context.getCommonRepository().fireVariantEvent(DocumentVariantEventType.DOCUMENT_VARIANT_CREATED, id, variant.getBranchId(), variant.getLanguageId(), variant.getUpdateCount()); 663 else 664 context.getCommonRepository().fireVariantEvent(DocumentVariantEventType.DOCUMENT_VARIANT_UPDATED, id, variant.getBranchId(), variant.getLanguageId(), variant.getUpdateCount()); 665 } 666 667 private void storeExtractedLinks(Connection conn, long documentId, long branchId, long languageId, Collection links) throws SQLException { 668 PreparedStatement stmt = null; 669 try { 670 stmt = conn.prepareStatement("delete from extracted_links where source_doc_id = ? and source_branch_id = ? and source_lang_id = ?"); 671 stmt.setLong(1, documentId); 672 stmt.setLong(2, branchId); 673 stmt.setLong(3, languageId); 674 stmt.execute(); 675 stmt.close(); 676 677 if (links.size() > 0) { 678 stmt = conn.prepareStatement("insert into extracted_links(source_doc_id, source_branch_id, source_lang_id, source_parttype_id, target_doc_id, target_branch_id, target_lang_id, target_version_id, linktype, in_last_version, in_live_version) values(?,?,?,?,?,?,?,?,?,?,?)"); 679 680 Iterator linksIt = links.iterator(); 681 while (linksIt.hasNext()) { 682 LinkInfo linkInfo = (LinkInfo)linksIt.next(); 683 684 stmt.setLong(1, linkInfo.sourceDocumentId); 685 stmt.setLong(2, linkInfo.sourceBranchId); 686 stmt.setLong(3, linkInfo.sourceLanguageId); 687 stmt.setLong(4, linkInfo.sourcePartTypeId); 688 stmt.setLong(5, linkInfo.targetDocumentId); 689 stmt.setLong(6, linkInfo.targetBranchId); 690 stmt.setLong(7, linkInfo.targetLanguageId); 691 stmt.setLong(8, linkInfo.targetVersionId); 692 stmt.setString(9, linkInfo.linkType.getCode()); 693 stmt.setBoolean(10, linkInfo.occursInLastVersion); 694 stmt.setBoolean(11, linkInfo.occursInLiveVersion); 695 696 stmt.addBatch(); 697 } 698 699 stmt.executeBatch(); 700 stmt.close(); 701 } 702 } finally { 703 jdbcHelper.closeStatement(stmt); 704 } 705 } 706 707 711 private String storeSummary(Connection conn, DocumentImpl document, long documentId, long versionId) throws SQLException { 712 PreparedStatement stmt = null; 713 try { 714 String summary = null; 715 try { 716 AuthenticatedUser user = document.getIntimateAccess(this).getCurrentUser(); 717 summary = context.getDocumentSummarizer().getSummary(document, versionId, new RepositorySchemaImpl(context.getRepositorySchema(), user)); 718 } catch (Exception e) { 719 logger.error("Error creating summary for document ID " + documentId + ", branch " + getBranchLabel(document.getBranchId()) + ", language " + getLanguageLabel(document.getLanguageId()), e); 720 } 721 if (summary != null) { 722 stmt = conn.prepareStatement("update summaries set summary=? where doc_id = ? and branch_id = ? and lang_id = ?"); 723 stmt.setString(1, summary); 724 stmt.setLong(2, documentId); 725 stmt.setLong(3, document.getBranchId()); 726 stmt.setLong(4, document.getLanguageId()); 727 long updateCount = stmt.executeUpdate(); 728 stmt.close(); 729 730 if (updateCount == 0) { 731 stmt = conn.prepareStatement("insert into summaries(doc_id, branch_id, lang_id, summary) values(?,?,?,?)"); 732 stmt.setLong(1, documentId); 733 stmt.setLong(2, document.getBranchId()); 734 stmt.setLong(3, document.getLanguageId()); 735 stmt.setString(4, summary); 736 stmt.execute(); 737 stmt.close(); 738 } 739 } else { 740 stmt = conn.prepareStatement("delete from summaries where doc_id = ? and branch_id = ? and lang_id = ?"); 741 stmt.setLong(1, documentId); 742 stmt.setLong(2, document.getBranchId()); 743 stmt.setLong(3, document.getLanguageId()); 744 stmt.execute(); 745 stmt.close(); 746 } 747 return summary; 748 } finally { 749 jdbcHelper.closeStatement(stmt); 750 } 751 } 752 753 private void storeCollections(DocumentVariantImpl variant, long id, Connection conn) throws SQLException, RepositoryException { 754 clearCollections(id, variant.getBranchId(), variant.getLanguageId(), conn); 756 addDocumentToCollections(variant.getIntimateAccess(this).getDocumentCollectionImpls(), id, variant.getBranchId(), variant.getLanguageId(), conn); 758 } 759 760 private void clearCollections(long documentId, long branchId, long languageId, Connection conn) throws SQLException { 761 PreparedStatement stmt = null; 762 763 try { 764 stmt = conn.prepareStatement("delete from document_collections where document_id=? and branch_id=? and lang_id=?"); 765 stmt.setLong(1, documentId); 766 stmt.setLong(2, branchId); 767 stmt.setLong(3, languageId); 768 stmt.executeUpdate(); 769 } finally { 770 jdbcHelper.closeStatement(stmt); 771 } 772 } 773 774 private long createVersion(DocumentImpl document, DocumentVariantImpl.IntimateAccess variantInt, long documentId, 775 Date now, Connection conn) throws SQLException { 776 PreparedStatement stmt = null; 777 try { 778 long newVersionId = 1; 779 780 if (document.getId() != -1) { 781 stmt = conn.prepareStatement("select max(id) from document_versions where doc_id = ? and branch_id = ? and lang_id = ?"); 783 stmt.setLong(1, document.getId()); 784 stmt.setLong(2, document.getBranchId()); 785 stmt.setLong(3, document.getLanguageId()); 786 ResultSet rs = stmt.executeQuery(); 787 rs.next(); 788 newVersionId = rs.getLong(1) + 1; 789 rs.close(); 790 stmt.close(); 791 } 792 793 long totalPartSize = 0L; 794 PartImpl[] parts = variantInt.getPartImpls(); 795 for (int i = 0; i < parts.length; i++) 796 totalPartSize += parts[i].getSize(); 797 798 long userId = variantInt.getCurrentUser().getId(); 799 stmt = conn.prepareStatement("insert into document_versions(id, doc_id, branch_id, lang_id, \"name\", created_on, created_by, state, state_last_modified, state_last_modifier, total_size_of_parts) values(?,?,?,?,?,?,?,?,?,?,?)"); 800 stmt.setLong(1, newVersionId); 801 stmt.setLong(2, documentId); 802 stmt.setLong(3, document.getBranchId()); 803 stmt.setLong(4, document.getLanguageId()); 804 stmt.setString(5, document.getName()); 805 stmt.setTimestamp(6, new Timestamp(now.getTime())); 806 stmt.setLong(7, userId); 807 stmt.setString(8, document.getNewVersionState().getCode()); 808 stmt.setTimestamp(9, new Timestamp(now.getTime())); 809 stmt.setLong(10, userId); 810 stmt.setLong(11, totalPartSize); 811 stmt.execute(); 812 stmt.close(); 813 814 return newVersionId; 815 } finally { 816 jdbcHelper.closeStatement(stmt); 817 } 818 } 819 820 private void storeCustomFields(DocumentImpl document, long documentId, Connection conn) throws SQLException { 821 PreparedStatement stmt = null; 822 try { 823 if (document.getId() != -1) { 824 stmt = conn.prepareStatement("delete from customfields where doc_id = ? and branch_id = ? and lang_id = ?"); 825 stmt.setLong(1, document.getId()); 826 stmt.setLong(2, document.getBranchId()); 827 stmt.setLong(3, document.getLanguageId()); 828 stmt.execute(); 829 stmt.close(); 830 } 831 832 Iterator customFieldsIt = document.getCustomFields().entrySet().iterator(); 833 while (customFieldsIt.hasNext()) { 834 Map.Entry entry = (Map.Entry)customFieldsIt.next(); 835 836 stmt = conn.prepareStatement("insert into customfields(doc_id, branch_id, lang_id, \"name\", \"value\") values(?,?,?,?,?)"); 837 stmt.setLong(1, documentId); 838 stmt.setLong(2, document.getBranchId()); 839 stmt.setLong(3, document.getLanguageId()); 840 stmt.setString(4, (String )entry.getKey()); 841 stmt.setString(5, (String )entry.getValue()); 842 stmt.execute(); 843 } 844 } finally { 845 jdbcHelper.closeStatement(stmt); 846 } 847 } 848 849 private List storeParts(DocumentVariantImpl variant, DocumentVariantImpl.IntimateAccess variantInt, long documentId, long versionId, Connection conn) throws SQLException, RepositoryException { 850 PreparedStatement stmt = null; 851 try { 852 PartImpl[] parts = variantInt.getPartImpls(); 853 List partUpdates = new ArrayList(parts.length); 854 for (int i = 0; i < parts.length; i++) { 855 PartImpl.IntimateAccess partInt = parts[i].getIntimateAccess(this); 856 String oldBlobKey = partInt.getBlobKey(); 857 String blobKey = partInt.getBlobKey(); 858 if (partInt.isDataUpdated()) { 859 try { 861 PartDataSource partDataSource = partInt.getPartDataSource(); 862 blobKey = context.getBlobStore().store(partDataSource.createInputStream()); 863 } catch (Exception e) { 864 throw new RepositoryException("Error storing part data to blobstore.", e); 865 } 866 partUpdates.add(new PartUpdate(partInt, blobKey, oldBlobKey)); 867 partInt.setBlobKey(blobKey); 872 } 873 874 if (stmt == null) { 876 stmt = conn.prepareStatement("insert into parts(doc_id, branch_id, lang_id, version_id, blob_id, mimetype, filename, parttype_id, blob_size) values(?,?,?,?,?,?,?,?,?)"); 877 } 878 879 stmt.setLong(1, documentId); 880 stmt.setLong(2, variant.getBranchId()); 881 stmt.setLong(3, variant.getLanguageId()); 882 stmt.setLong(4, versionId); 883 stmt.setString(5, blobKey); 884 stmt.setString(6, parts[i].getMimeType()); 885 stmt.setString(7, parts[i].getFileName()); 886 stmt.setLong(8, parts[i].getTypeId()); 887 stmt.setLong(9, parts[i].getSize()); 888 stmt.execute(); 889 } 890 return partUpdates; 891 } finally { 892 jdbcHelper.closeStatement(stmt); 893 } 894 } 895 896 private class PartUpdate { 897 private final PartImpl.IntimateAccess partInt; 898 private final String blobKey; 899 private final String oldBlobKey; 900 private final PartDataSource partDataSource; 901 902 public PartUpdate(PartImpl.IntimateAccess partInt, String blobKey, String oldBlobKey) { 903 this.partInt = partInt; 904 this.blobKey = blobKey; 905 this.oldBlobKey = oldBlobKey; 906 this.partDataSource = partInt.getPartDataSource(); 907 partInt.setData(null); 908 } 909 910 public void rollback() { 911 partInt.setBlobKey(oldBlobKey); 912 partInt.setData(partDataSource); 913 try { 914 context.getBlobStore().delete(blobKey); 915 } catch (Throwable e) { 916 logger.error("Problem removing blob \"" + blobKey + "\" after failed document update. Ignoring.", e); 917 } 918 } 919 } 920 921 private void storeFields(DocumentImpl document, long documentId, long versionId, Connection conn) throws SQLException { 922 PreparedStatement stmt = null; 923 try { 924 stmt = conn.prepareStatement("insert into thefields(doc_id, branch_id, lang_id, version_id, fieldtype_id, value_seq, value_count, stringvalue, datevalue, datetimevalue, integervalue, floatvalue, decimalvalue, booleanvalue, link_docid, link_branchid, link_searchbranchid, link_langid, link_searchlangid, link_search) values (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"); 926 stmt.setLong(1, documentId); 927 stmt.setLong(2, document.getBranchId()); 928 stmt.setLong(3, document.getLanguageId()); 929 stmt.setLong(4, versionId); 930 Field[] fields = document.getFields().getArray(); 931 for (int i = 0; i < fields.length; i++) { 932 stmt.setLong(5, fields[i].getTypeId()); 933 934 ValueType fieldType = fields[i].getValueType(); 935 Object [] values = fields[i].isMultiValue() ? (Object [])fields[i].getValue() : new Object [] {fields[i].getValue()}; 936 937 stmt.setLong(7, values.length); 938 for (int k = 0; k < values.length; k++) { 939 Object value = values[k]; 940 String stringValue = (String )(fieldType == ValueType.STRING ? value : null); 941 Date dateValue = (fieldType == ValueType.DATE ? new java.sql.Date (((Date )value).getTime()) : null); 942 Timestamp datetimeValue = (fieldType == ValueType.DATETIME ? new Timestamp(((Date )value).getTime()) : null); 943 Long integerValue = (Long )(fieldType == ValueType.LONG ? value : null); 944 Double floatValue = (Double )(fieldType == ValueType.DOUBLE ? value : null); 945 BigDecimal decimalValue = (BigDecimal )(fieldType == ValueType.DECIMAL ? value : null); 946 Boolean booleanValue = (Boolean )(fieldType == ValueType.BOOLEAN ? value : null); 947 948 stmt.setInt(6, k + 1); 949 stmt.setString(8, stringValue); 950 stmt.setObject(9, dateValue); 951 stmt.setObject(10, datetimeValue); 952 stmt.setObject(11, integerValue); 953 stmt.setObject(12, floatValue); 954 stmt.setBigDecimal(13, decimalValue); 955 stmt.setObject(14, booleanValue); 956 957 if (fieldType == ValueType.LINK) { 958 VariantKey variantKey = (VariantKey)value; 959 long branchId = variantKey.getBranchId() == -1 ? document.getBranchId() : variantKey.getBranchId(); 960 long languageId = variantKey.getLanguageId() == -1 ? document.getLanguageId() : variantKey.getLanguageId(); 961 962 stmt.setLong(15, variantKey.getDocumentId()); 963 stmt.setLong(16, variantKey.getBranchId()); 964 stmt.setLong(17, branchId); 965 stmt.setLong(18, variantKey.getLanguageId()); 966 stmt.setLong(19, languageId); 967 stmt.setString(20, variantKey.getDocumentId() + "@" + branchId + ":" + languageId); 968 } else { 969 stmt.setNull(15, Types.BIGINT); 970 stmt.setNull(16, Types.BIGINT); 971 stmt.setNull(17, Types.BIGINT); 972 stmt.setNull(18, Types.BIGINT); 973 stmt.setNull(19, Types.BIGINT); 974 stmt.setNull(20, Types.VARCHAR); 975 } 976 977 stmt.execute(); 978 } 979 } 980 stmt.close(); 981 } finally { 982 jdbcHelper.closeStatement(stmt); 983 } 984 } 985 986 private void storeLinks(DocumentImpl document, long documentId, long versionId, Connection conn) throws SQLException { 987 PreparedStatement stmt = null; 988 try { 989 stmt = conn.prepareStatement("insert into links(id, doc_id, branch_id, lang_id, version_id, title, target) values (?,?,?,?,?,?,?)"); 990 stmt.setLong(2, documentId); 991 stmt.setLong(3, document.getBranchId()); 992 stmt.setLong(4, document.getLanguageId()); 993 stmt.setLong(5, versionId); 994 Link[] links = document.getLinks().getArray(); 995 for (int i = 0; i < links.length; i++) { 996 stmt.setLong(1, i); 997 stmt.setString(6, links[i].getTitle()); 998 stmt.setString(7, links[i].getTarget()); 999 stmt.execute(); 1000 } 1001 stmt.close(); 1002 } finally { 1003 jdbcHelper.closeStatement(stmt); 1004 } 1005 } 1006 1007 public InputStream getBlob(long documentId, long branchId, long languageId, long versionId, long partTypeId, AuthenticatedUser user) throws RepositoryException { 1008 Document document = context.getCommonRepository().getDocument(documentId, branchId, languageId, false, user); 1009 return document.getVersion(versionId).getPart(partTypeId).getDataStream(); 1010 } 1011 1012 public InputStream getBlob(String blobKey) throws RepositoryException { 1013 try { 1014 return context.getBlobStore().retrieve(blobKey); 1015 } catch (BlobIOException e) { 1016 throw new RepositoryException("Problem retrieving blob data.", e); 1017 } catch (NonExistingBlobException e) { 1018 throw new RepositoryException("Problem retrieving blob data.", e); 1019 } 1020 } 1021 1022 public VersionImpl[] loadShallowVersions(DocumentVariantImpl variant) throws RepositoryException { 1023 DocumentVariantImpl.IntimateAccess variantInt = variant.getIntimateAccess(this); 1024 VersionImpl[] versions = new VersionImpl[(int)variantInt.getLastVersionId()]; 1025 1026 Connection conn = null; 1027 PreparedStatement stmt = null; 1028 try { 1029 conn = context.getDataSource().getConnection(); 1030 stmt = conn.prepareStatement("select id, created_on, created_by, \"name\", state, state_last_modified, state_last_modifier, total_size_of_parts from document_versions where doc_id = ? and branch_id = ? and lang_id = ? order by id ASC"); 1031 stmt.setLong(1, variant.getDocumentId()); 1032 stmt.setLong(2, variant.getBranchId()); 1033 stmt.setLong(3, variant.getLanguageId()); 1034 ResultSet rs = stmt.executeQuery(); 1035 1036 for (int i = 0; i < versions.length; i++) { 1037 rs.next(); 1038 Date createdOn = rs.getTimestamp("created_on"); 1039 long createdBy = rs.getLong("created_by"); 1040 String documentName = rs.getString("name"); 1041 long versionId = rs.getLong("id"); 1042 VersionState versionState = VersionState.getByCode(rs.getString("state")); 1043 Date versionStateLastModified = rs.getTimestamp("state_last_modified"); 1044 long versionStateLastModifier = rs.getLong("state_last_modifier"); 1045 long totalSizeOfParts = rs.getLong("total_size_of_parts"); 1046 1047 versions[i] = new VersionImpl(variantInt, versionId, documentName, createdOn, createdBy, 1048 versionState, versionStateLastModified, versionStateLastModifier, totalSizeOfParts); 1049 } 1050 stmt.close(); 1051 } catch (Throwable e) { 1052 throw new RepositoryException("Error loading versions (document ID: " + variant.getDocumentId() + ", branch: " + getBranchLabel(variant.getBranchId()) + ", language: " + getLanguageLabel(variant.getLanguageId()) + ").", e); 1053 } finally { 1054 jdbcHelper.closeStatement(stmt); 1055 jdbcHelper.closeConnection(conn); 1056 } 1057 1058 return versions; 1059 } 1060 1061 public VersionImpl loadVersion(DocumentVariantImpl variant, long versionId) throws RepositoryException { 1062 DocumentVariantImpl.IntimateAccess variantInt = variant.getIntimateAccess(this); 1063 Connection conn = null; 1064 PreparedStatement stmt = null; 1065 try { 1066 conn = context.getDataSource().getConnection(); 1067 jdbcHelper.startTransaction(conn); 1068 1069 stmt = conn.prepareStatement("select created_on, created_by, \"name\", state, state_last_modified, state_last_modifier, total_size_of_parts from document_versions where id = ? and doc_id = ? and branch_id = ? and lang_id = ?"); 1070 stmt.setLong(1, versionId); 1071 stmt.setLong(2, variant.getDocumentId()); 1072 stmt.setLong(3, variant.getBranchId()); 1073 stmt.setLong(4, variant.getLanguageId()); 1074 ResultSet rs = stmt.executeQuery(); 1075 if (!rs.next()) 1076 throw new RepositoryException("Unexpected problem: record for version not found (document ID: " + variant.getDocumentId() + ", branch: " + getBranchLabel(variant.getBranchId()) + ", language: " + getLanguageLabel(variant.getLanguageId()) + ", version ID: " + versionId + ")."); 1077 Date createdOn = rs.getTimestamp("created_on"); 1078 long createdBy = rs.getLong("created_by"); 1079 String documentName = rs.getString("name"); 1080 VersionState versionState = VersionState.getByCode(rs.getString("state")); 1081 Date versionStateLastModified = rs.getTimestamp("state_last_modified"); 1082 long versionStateLastModifier = rs.getLong("state_last_modifier"); 1083 long totalSizeOfParts = rs.getLong("total_size_of_parts"); 1084 stmt.close(); 1085 1086 FieldImpl[] fields = (FieldImpl[])loadFields(variant, versionId, conn).toArray(new FieldImpl[0]); 1087 PartImpl[] parts = (PartImpl[])loadParts(variant, variantInt, versionId, conn).toArray(new PartImpl[0]); 1088 LinkImpl[] links = (LinkImpl[])loadLinks(variant, versionId, conn).toArray(new LinkImpl[0]); 1089 1090 VersionImpl versionImpl = new VersionImpl(variantInt, versionId, createdOn, createdBy, documentName, parts, 1091 fields, links, versionState, versionStateLastModified, versionStateLastModifier, totalSizeOfParts); 1092 return versionImpl; 1093 } catch (Throwable e) { 1094 throw new RepositoryException("Error loading version (document ID: " + variant.getDocumentId() + ", branch: " + getBranchLabel(variant.getBranchId()) + ", language: " + getLanguageLabel(variant.getLanguageId()) + ", version ID: " + versionId + ").", e); 1095 } finally { 1096 jdbcHelper.closeStatement(stmt); 1097 jdbcHelper.closeConnection(conn); 1098 } 1099 } 1100 1101 public void completeVersion(DocumentVariantImpl variant, VersionImpl version) throws RepositoryException { 1102 DocumentVariantImpl.IntimateAccess variantInt = variant.getIntimateAccess(this); 1103 VersionImpl.IntimateAccess versionInt = version.getIntimateAccess(this); 1104 Connection conn = null; 1105 try { 1106 conn = context.getDataSource().getConnection(); 1107 jdbcHelper.startTransaction(conn); 1108 1109 PartImpl[] parts = (PartImpl[]) loadParts(variant, variantInt, version.getId(), conn).toArray(new PartImpl[0]); 1110 FieldImpl[] fields = (FieldImpl[]) loadFields(variant, version.getId(), conn).toArray(new FieldImpl[0]); 1111 LinkImpl[] links = (LinkImpl[]) loadLinks(variant, version.getId(), conn).toArray(new LinkImpl[0]); 1112 1113 versionInt.setParts(parts); 1114 versionInt.setFields(fields); 1115 versionInt.setLinks(links); 1116 } catch (Throwable e) { 1117 throw new RepositoryException("Error loading version (document ID: " + variant.getDocumentId() + ", branch: " + getBranchLabel(variant.getBranchId()) + ", language: " + getLanguageLabel(variant.getLanguageId()) + ", Version ID: " + version.getId() + ").", e); 1118 } finally { 1119 jdbcHelper.closeConnection(conn); 1120 } 1121 } 1122 1123 public void setVersionState(DocumentImpl document, VersionImpl version, VersionState versionState) throws RepositoryException { 1124 DocumentImpl.IntimateAccess documentInt = document.getIntimateAccess(this); 1125 Connection conn = null; 1126 PreparedStatement stmt = null; 1127 try { 1128 AclResultInfo aclInfo = context.getCommonRepository().getAccessManager().getAclInfoOnLive(systemUser, 1130 documentInt.getCurrentUser().getId(), documentInt.getCurrentUser().getActiveRoleIds(), 1131 document.getId(), document.getBranchId(), document.getLanguageId()); 1132 if (!aclInfo.isAllowed(AclPermission.PUBLISH)) 1133 throw new AccessException("User " + documentInt.getCurrentUser().getLogin() + " (ID: " + documentInt.getCurrentUser().getId() + ") is not allowed to change the state of versions of document ID " + document.getId() + ", branch ID " + document.getBranchId() + ", language ID " + document.getLanguageId()); 1134 1135 if (logger.isDebugEnabled()) { 1136 logger.debug("Changing version state for document ID " + document.getId() + ", branch " + getBranchLabel(document.getBranchId()) + ", language " + getLanguageLabel(document.getLanguageId()) + ", version " + version.getId() + " to " + versionState); 1137 } 1138 1139 conn = context.getDataSource().getConnection(); 1140 jdbcHelper.startTransaction(conn); 1141 1142 stmt = conn.prepareStatement("select last_modified from document_variants where doc_id = ? and branch_id = ? and lang_id = ? " + jdbcHelper.getSharedLockClause()); 1144 stmt.setLong(1, document.getId()); 1145 stmt.setLong(2, document.getBranchId()); 1146 stmt.setLong(3, document.getLanguageId()); 1147 stmt.execute(); 1148 stmt.close(); 1149 1150 Date lastModified = new Date (); 1151 long lastModifier = documentInt.getCurrentUser().getId(); 1152 stmt = conn.prepareStatement("update document_versions set state=?, state_last_modified=?, state_last_modifier=? where doc_id = ? and branch_id = ? and lang_id = ? and id = ?"); 1154 stmt.setString(1, versionState.getCode()); 1155 stmt.setTimestamp(2, new Timestamp(lastModified.getTime())); 1156 stmt.setLong(3, lastModifier); 1157 stmt.setLong(4, document.getId()); 1158 stmt.setLong(5, document.getBranchId()); 1159 stmt.setLong(6, document.getLanguageId()); 1160 stmt.setLong(7, version.getId()); 1161 long updateCount = stmt.executeUpdate(); 1162 if (updateCount != 1) 1163 throw new RepositoryException("Assertion failed: wrong number of rows updated (when updating state):" + updateCount); 1164 stmt.close(); 1165 1166 stmt = conn.prepareStatement("select liveversion_id from document_variants where doc_id = ? and branch_id = ? and lang_id = ?"); 1169 stmt.setLong(1, document.getId()); 1170 stmt.setLong(2, document.getBranchId()); 1171 stmt.setLong(3, document.getLanguageId()); 1172 ResultSet rs = stmt.executeQuery(); 1173 rs.next(); 1174 long previousLiveVersionId = rs.getLong(1); 1175 stmt.close(); 1176 1177 long liveVersionId = getLastPublishedVersionId(conn, document.getId(), document.getBranchId(), document.getLanguageId()); 1178 stmt = conn.prepareStatement("update document_variants set liveversion_id = ? where doc_id = ? and branch_id = ? and lang_id = ?"); 1179 stmt.setLong(1, liveVersionId); 1180 stmt.setLong(2, document.getId()); 1181 stmt.setLong(3, document.getBranchId()); 1182 stmt.setLong(4, document.getLanguageId()); 1183 updateCount = stmt.executeUpdate(); 1184 if (updateCount != 1) 1185 throw new RepositoryException("Assertion failed: wrong number of rows updated (when updating live version id):" + updateCount); 1186 stmt.close(); 1187 1188 String summary = null; 1190 boolean publishedVersionChanged = previousLiveVersionId != liveVersionId; 1191 if (publishedVersionChanged) { 1192 LinkExtractorHelper linkExtractorHelper = new LinkExtractorHelper(document, document.getId(), liveVersionId, document.getLastVersionId(), false, systemUser, context); 1193 Collection links = linkExtractorHelper.extract(); 1194 storeExtractedLinks(conn, document.getId(), document.getBranchId(), document.getLanguageId(), links); 1195 summary = storeSummary(conn, document, document.getId(), liveVersionId); 1196 } 1197 1198 VersionStateChangedDocument versionStateChangedDocument = VersionStateChangedDocument.Factory.newInstance(); 1200 VersionStateChangedDocument.VersionStateChanged versionStateChanged = versionStateChangedDocument.addNewVersionStateChanged(); 1201 versionStateChanged.setDocumentId(document.getId()); 1202 versionStateChanged.setBranchId(document.getBranchId()); 1203 versionStateChanged.setLanguageId(document.getLanguageId()); 1204 versionStateChanged.setVersionId(version.getId()); 1205 versionStateChanged.setNewState(versionState.toString()); 1206 versionStateChanged.setPreviousLiveVersionId(previousLiveVersionId); 1207 versionStateChanged.setLiveVersionId(liveVersionId); 1208 versionStateChanged.setModifier(lastModifier); 1209 versionStateChanged.setModified(getCalendar(lastModified)); 1210 eventHelper.createEvent(versionStateChangedDocument, "VersionStateChanged", conn); 1211 1212 conn.commit(); 1213 1214 version.getIntimateAccess(this).stateChanged(versionState, lastModified, lastModifier); 1215 if (publishedVersionChanged) 1216 documentInt.getVariant().getIntimateAccess(this).setSummary(summary); 1217 } catch (Throwable e) { 1218 jdbcHelper.rollback(conn); 1219 if (e instanceof AccessException) 1220 throw (AccessException)e; 1221 throw new RepositoryException("Error trying to change version state.", e); 1222 } finally { 1223 jdbcHelper.closeStatement(stmt); 1224 jdbcHelper.closeConnection(conn); 1225 } 1226 context.getCommonRepository().fireVariantEvent(DocumentVariantEventType.VERSION_STATE_CHANGED, document.getId(), document.getBranchId(), document.getLanguageId(), -1); 1227 } 1228 1229 private long getLastPublishedVersionId(Connection conn, long documentId, long branchId, long languageId) throws SQLException { 1230 PreparedStatement stmt = null; 1231 try { 1232 stmt = conn.prepareStatement("select max(id) from document_versions where doc_id = ? and branch_id = ? and lang_id = ? and state = 'P'"); 1233 stmt.setLong(1, documentId); 1234 stmt.setLong(2, branchId); 1235 stmt.setLong(3, languageId); 1236 ResultSet rs = stmt.executeQuery(); 1237 rs.next(); 1238 long versionId = rs.getLong(1); 1239 if (versionId == 0) 1240 return -1; 1241 else 1242 return versionId; 1243 } finally { 1244 jdbcHelper.closeStatement(stmt); 1245 } 1246 } 1247 1248 public LockInfoImpl lock(DocumentVariantImpl variant, long duration, LockType lockType) throws RepositoryException { 1249 DocumentVariantImpl.IntimateAccess variantInt = variant.getIntimateAccess(this); 1250 1251 AclResultInfo aclInfo = context.getCommonRepository().getAccessManager().getAclInfoOnLive(systemUser, 1253 variantInt.getCurrentUser().getId(), variantInt.getCurrentUser().getActiveRoleIds(), 1254 variant.getDocumentId(), variant.getBranchId(), variant.getLanguageId()); 1255 if (!aclInfo.isAllowed(AclPermission.WRITE)) 1256 throw new AccessException("Write access denied for user " + variantInt.getCurrentUser().getId() + " to document ID " + variant.getDocumentId() + ", branch ID " + variant.getBranchId() + ", language ID " + variant.getLanguageId()); 1257 1258 List executeAfterCommit = new ArrayList(2); 1259 Connection conn = null; 1260 PreparedStatement stmt = null; 1261 LockInfoImpl lockInfo = null; 1262 try { 1263 conn = context.getDataSource().getConnection(); 1264 jdbcHelper.startTransaction(conn); 1265 1266 stmt = conn.prepareStatement("select doc_id from document_variants where doc_id = ? and branch_id = ? and lang_id = ? " + jdbcHelper.getSharedLockClause()); 1269 stmt.setLong(1, variant.getDocumentId()); 1270 stmt.setLong(2, variant.getBranchId()); 1271 stmt.setLong(3, variant.getLanguageId()); 1272 stmt.execute(); 1273 stmt.close(); 1274 1275 lockInfo = loadLock(variant.getDocumentId(), variant.getBranchId(), variant.getLanguageId(), conn, true, executeAfterCommit); 1277 1278 if (lockInfo.hasLock() && lockInfo.getType() == LockType.PESSIMISTIC 1281 && lockInfo.getUserId() != variantInt.getCurrentUser().getId()) { 1282 return lockInfo; 1283 } 1284 1285 if (lockInfo.hasLock()) { 1286 stmt = conn.prepareStatement("delete from locks where doc_id = ? and branch_id = ? and lang_id = ?"); 1288 stmt.setLong(1, variant.getDocumentId()); 1289 stmt.setLong(2, variant.getBranchId()); 1290 stmt.setLong(3, variant.getLanguageId()); 1291 stmt.execute(); 1292 stmt.close(); 1293 } 1294 1295 lockInfo = new LockInfoImpl(variantInt.getCurrentUser().getId(), new Date (), duration, lockType); 1297 stmt = conn.prepareStatement("insert into locks(doc_id, branch_id, lang_id, user_id, locktype, time_acquired, duration, time_expires) values(?,?,?,?,?,?,?,?)"); 1298 stmt.setLong(1, variant.getDocumentId()); 1299 stmt.setLong(2, variant.getBranchId()); 1300 stmt.setLong(3, variant.getLanguageId()); 1301 stmt.setLong(4, lockInfo.getUserId()); 1302 stmt.setString(5, lockInfo.getType().getCode()); 1303 stmt.setTimestamp(6, new Timestamp(lockInfo.getTimeAcquired().getTime())); 1304 stmt.setLong(7, lockInfo.getDuration()); 1305 if (lockInfo.getDuration() >= 0) 1306 stmt.setTimestamp(8, new Timestamp(lockInfo.getTimeAcquired().getTime() + lockInfo.getDuration())); 1307 else 1308 stmt.setNull(8, Types.TIMESTAMP); 1309 stmt.execute(); 1310 conn.commit(); 1311 } catch (Throwable e) { 1312 jdbcHelper.rollback(conn); 1313 executeAfterCommit.clear(); 1314 throw new RepositoryException("Error while getting a lock on document ID " + variant.getDocumentId() + ", branch " + getBranchLabel(variant.getBranchId()) + ", language " + getLanguageLabel(variant.getLanguageId()), e); 1315 } finally { 1316 jdbcHelper.closeStatement(stmt); 1317 jdbcHelper.closeConnection(conn); 1318 executeRunnables(executeAfterCommit); 1319 } 1320 1321 context.getCommonRepository().fireVariantEvent(DocumentVariantEventType.LOCK_CHANGE, variant.getDocumentId(), variant.getBranchId(), variant.getLanguageId(), -1); 1323 1324 return lockInfo; 1325 } 1326 1327 public LockInfoImpl getLockInfo(DocumentVariantImpl variant) throws RepositoryException { 1328 Connection conn = null; 1329 try { 1330 conn = context.getDataSource().getConnection(); 1331 jdbcHelper.startTransaction(conn); 1332 List executeAfterCommit = new ArrayList(2); 1333 LockInfoImpl lockInfo = loadLock(variant.getDocumentId(), variant.getBranchId(), variant.getLanguageId(), conn, false, executeAfterCommit); 1334 conn.commit(); 1335 executeRunnables(executeAfterCommit); 1336 return lockInfo; 1337 } catch (Exception e) { 1338 throw new RepositoryException("Error loading lock info.", e); 1339 } finally { 1340 jdbcHelper.closeConnection(conn); 1341 } 1342 } 1343 1344 1358 private LockInfoImpl loadLock(final long documentId, final long branchId, final long languageId, Connection conn, boolean sharedModeLock, List executeAfterCommit) throws SQLException { 1359 PreparedStatement stmt = null; 1360 try { 1361 stmt = conn.prepareStatement("select user_id, locktype, time_acquired, duration from locks where doc_id = ? and branch_id = ? and lang_id = ? " + (sharedModeLock ? jdbcHelper.getSharedLockClause(): "")); 1362 stmt.setLong(1, documentId); 1363 stmt.setLong(2, branchId); 1364 stmt.setLong(3, languageId); 1365 ResultSet rs = stmt.executeQuery(); 1366 if (!rs.next()) 1367 return new LockInfoImpl(); 1368 long userId = rs.getLong("user_id"); 1369 LockType locktype = LockType.getByCode(rs.getString("locktype")); 1370 Date timeAcquired = rs.getTimestamp("time_acquired"); 1371 long duration = rs.getLong("duration"); 1372 stmt.close(); 1373 1374 if (duration != -1 && timeAcquired.getTime() + duration < System.currentTimeMillis()) { 1376 if (logger.isDebugEnabled()) 1377 logger.debug("Removing expired lock for user " + userId + " on document ID " + documentId + ", branch " + getBranchLabel(branchId) + ", language " + getLanguageLabel(languageId)); 1378 stmt = conn.prepareStatement("delete from locks where doc_id = ? and branch_id = ? and lang_id = ? and user_id = ? and locktype = ? and time_acquired = ? and duration = ?"); 1382 stmt.setLong(1, documentId); 1383 stmt.setLong(2, branchId); 1384 stmt.setLong(3, languageId); 1385 stmt.setLong(4, userId); 1386 stmt.setString(5, locktype.getCode()); 1387 stmt.setTimestamp(6, new Timestamp(timeAcquired.getTime())); 1388 stmt.setLong(7, duration); 1389 stmt.execute(); 1390 stmt.close(); 1391 1392 executeAfterCommit.add(new Runnable () { 1393 public void run() { 1394 context.getCommonRepository().fireVariantEvent(DocumentVariantEventType.LOCK_CHANGE, documentId, branchId, languageId, -1); 1396 } 1397 }); 1398 return new LockInfoImpl(); 1399 } 1400 return new LockInfoImpl(userId, timeAcquired, duration, locktype); 1401 } finally { 1402 jdbcHelper.closeStatement(stmt); 1403 } 1404 } 1405 1406 public LockInfoImpl releaseLock(DocumentVariantImpl variant) throws RepositoryException { 1407 DocumentVariantImpl.IntimateAccess variantInt = variant.getIntimateAccess(this); 1408 1409 AclResultInfo aclInfo = context.getCommonRepository().getAccessManager().getAclInfoOnLive(systemUser, 1411 variantInt.getCurrentUser().getId(), variantInt.getCurrentUser().getActiveRoleIds(), 1412 variant.getDocumentId(), variant.getBranchId(), variant.getLanguageId()); 1413 if (!aclInfo.isAllowed(AclPermission.WRITE)) 1414 throw new AccessException("Write access denied for user " + variantInt.getCurrentUser().getId() + " to document ID " + variant.getDocumentId() + ", branch " + getBranchLabel(variant.getBranchId()) + ", language " + getLanguageLabel(variant.getLanguageId())); 1415 1416 List executeAfterCommit = new ArrayList(2); 1417 Connection conn = null; 1418 PreparedStatement stmt = null; 1419 try { 1420 conn = context.getDataSource().getConnection(); 1421 jdbcHelper.startTransaction(conn); 1422 1423 LockInfoImpl lockInfo = loadLock(variant.getDocumentId(), variant.getBranchId(), variant.getLanguageId(), conn, true, executeAfterCommit); 1424 if (!lockInfo.hasLock()) 1425 return lockInfo; 1426 1427 if (lockInfo.getUserId() != variantInt.getCurrentUser().getId()) 1428 return lockInfo; 1429 else if (lockInfo.getUserId() != variantInt.getCurrentUser().getId() && !variantInt.getCurrentUser().isInAdministratorRole()) 1430 return lockInfo; 1431 1432 stmt = conn.prepareStatement("delete from locks where doc_id = ? and branch_id = ? and lang_id = ?"); 1433 stmt.setLong(1, variant.getDocumentId()); 1434 stmt.setLong(2, variant.getBranchId()); 1435 stmt.setLong(3, variant.getLanguageId()); 1436 stmt.execute(); 1437 conn.commit(); 1438 1439 context.getCommonRepository().fireVariantEvent(DocumentVariantEventType.LOCK_CHANGE, variant.getDocumentId(), variant.getBranchId(), variant.getLanguageId(), -1); 1441 1442 return new LockInfoImpl(); 1443 } catch (Throwable e) { 1444 jdbcHelper.rollback(conn); 1445 executeAfterCommit.clear(); 1446 throw new RepositoryException("Error removing lock.", e); 1447 } finally { 1448 jdbcHelper.closeStatement(stmt); 1449 jdbcHelper.closeConnection(conn); 1450 executeRunnables(executeAfterCommit); 1451 } 1452 } 1453 1454 1455 1461 1462 private Collection loadCollections(DocumentVariantImpl variant, Connection conn) throws RepositoryException { 1463 PreparedStatement stmt = null; 1464 ArrayList list = new ArrayList(); 1465 PreparedStatement substmt = null; 1466 boolean anotherRecordFound; 1467 1468 try { 1469 DocumentVariantImpl.IntimateAccess variantInt = variant.getIntimateAccess(this); 1470 1471 stmt = conn.prepareStatement("select collection_id from document_collections where document_id = ? and branch_id = ? and lang_id = ?"); 1472 stmt.setLong(1, variant.getDocumentId()); 1473 stmt.setLong(2, variant.getBranchId()); 1474 stmt.setLong(3, variant.getLanguageId()); 1475 ResultSet rs = stmt.executeQuery(); 1476 1477 anotherRecordFound = rs.next(); 1478 if (!anotherRecordFound) { 1479 return null; 1480 } else { 1481 CommonCollectionManager collectionManager = context.getCommonRepository().getCollectionManager(); 1482 AuthenticatedUser user = variantInt.getCurrentUser(); 1483 while (anotherRecordFound) { 1484 long collectionId = rs.getLong(1); 1485 try { 1486 list.add(collectionManager.getCollection(collectionId, false, user)); 1487 } catch (CollectionNotFoundException e) { 1488 } 1491 anotherRecordFound = rs.next(); 1492 } 1493 } 1494 1495 } catch (Throwable e) { 1496 logger.debug(e.getMessage()); 1497 throw new RepositoryException("Error loading collections.", e); 1498 } finally { 1499 jdbcHelper.closeStatement(substmt); 1500 jdbcHelper.closeStatement(stmt); 1501 } 1502 1503 return list; 1504 } 1505 1506 private void addDocumentToCollections(DocumentCollectionImpl[] collArray, long docId, long branchId, long languageId, Connection conn) throws SQLException, RepositoryException { 1507 logger.debug("begin adding document to collection"); 1508 PreparedStatement stmt = null; 1509 1510 try { 1511 stmt = conn.prepareStatement("insert into document_collections(document_id, branch_id, lang_id, collection_id) values (?,?,?,?)"); 1512 for (int i = 0; i < collArray.length; i++) { 1513 DocumentCollectionImpl collToAddTo = collArray[i]; 1514 try { 1515 stmt.setLong(1, docId); 1516 stmt.setLong(2, branchId); 1517 stmt.setLong(3, languageId); 1518 stmt.setLong(4, collToAddTo.getId()); 1519 stmt.executeUpdate(); 1520 } catch (SQLException e) { 1521 1534 try { 1535 context.getCommonRepository().getCollectionManager().getCollection(collToAddTo.getId(), false, systemUser); 1536 } catch (RepositoryException e1) { 1537 if (e1 instanceof CollectionNotFoundException) 1538 throw new CollectionDeletedException(String.valueOf(collToAddTo.getId())); 1539 else throw e; 1542 } 1543 } 1544 } 1545 } finally { 1546 jdbcHelper.closeStatement(stmt); 1547 logger.debug("document addition to collection complete"); 1548 } 1549 } 1550 1551 private DocumentUpdatedDocument createDocumentUpdatedEvent(DocumentImpl oldDocument, DocumentImpl newDocument, Date lastModified, long newUpdateCount) { 1552 DocumentUpdatedDocument documentUpdatedDocument = DocumentUpdatedDocument.Factory.newInstance(); 1553 DocumentUpdatedDocument.DocumentUpdated documentUpdated = documentUpdatedDocument.addNewDocumentUpdated(); 1554 documentUpdated.addNewOldDocument().setDocument(oldDocument.getXmlWithoutVariant().getDocument()); 1555 1556 DocumentImpl.IntimateAccess newDocumentInt = newDocument.getIntimateAccess(this); 1557 DocumentDocument.Document newDocumentXml = newDocument.getXmlWithoutVariant().getDocument(); 1558 updateUpdatedDocumentXml(newDocumentXml, lastModified, newDocumentInt.getCurrentUser().getId(), newUpdateCount); 1559 documentUpdated.addNewNewDocument().setDocument(newDocumentXml); 1560 1561 return documentUpdatedDocument; 1562 } 1563 1564 1567 private void updateUpdatedDocumentXml(DocumentDocument.Document documentXml, Date lastModified, long lastModifier, long newUpdateCount) { 1568 documentXml.setUpdateCount(newUpdateCount); 1569 documentXml.setLastModified(getCalendar(lastModified)); 1570 documentXml.setLastModifier(lastModifier); 1571 } 1572 1573 private DocumentCreatedDocument createNewDocumentEvent(DocumentImpl document, long documentId, Date lastModified, long newUpdateCount) { 1574 DocumentImpl.IntimateAccess documentInt = document.getIntimateAccess(this); 1575 1576 DocumentCreatedDocument documentCreatedDocument = DocumentCreatedDocument.Factory.newInstance(); 1577 DocumentCreatedDocument.DocumentCreated documentCreated = documentCreatedDocument.addNewDocumentCreated(); 1578 DocumentCreatedDocument.DocumentCreated.NewDocument newDocument = documentCreated.addNewNewDocument(); 1579 1580 DocumentDocument.Document documentXml = document.getXmlWithoutVariant().getDocument(); 1581 updateNewDocumentXml(documentXml, documentId, lastModified, documentInt.getCurrentUser().getId(), newUpdateCount); 1582 newDocument.setDocument(documentXml); 1583 1584 return documentCreatedDocument; 1585 } 1586 1587 1590 private void updateNewDocumentXml(DocumentDocument.Document documentXml, long documentId, Date lastModified, long lastModifier, long newUpdateCount) { 1591 documentXml.setUpdateCount(newUpdateCount); 1592 documentXml.setLastModified(getCalendar(lastModified)); 1593 documentXml.setLastModifier(lastModifier); 1594 documentXml.setId(documentId); 1595 documentXml.setCreated(getCalendar(lastModified)); 1596 } 1597 1598 private DocumentVariantUpdatedDocument createVariantUpdatedEvent(DocumentImpl oldDocument, DocumentImpl newDocument, Date lastModified, String summary, long lastVersionId, long newVariantUpdateCount, long newDocumentUpdateCount) throws RepositoryException { 1599 DocumentVariantUpdatedDocument variantUpdatedDocument = DocumentVariantUpdatedDocument.Factory.newInstance(); 1600 DocumentVariantUpdatedDocument.DocumentVariantUpdated variantUpdated = variantUpdatedDocument.addNewDocumentVariantUpdated(); 1601 variantUpdated.addNewOldDocumentVariant().setDocument(oldDocument.getXml().getDocument()); 1602 1603 DocumentImpl.IntimateAccess newDocumentInt = newDocument.getIntimateAccess(this); 1604 DocumentDocument.Document newDocumentXml = newDocument.getXml().getDocument(); 1605 updateUpdatedDocumentXml(newDocumentXml, lastModified, newDocumentInt.getCurrentUser().getId(), newDocumentUpdateCount); 1606 newDocumentXml.setVariantUpdateCount(newVariantUpdateCount); 1609 newDocumentXml.setVariantLastModified(getCalendar(lastModified)); 1610 newDocumentXml.setVariantLastModifier(newDocumentInt.getCurrentUser().getId()); 1611 newDocumentXml.setLastVersionId(lastVersionId); 1612 newDocumentXml.setSummary(summary); 1613 variantUpdated.addNewNewDocumentVariant().setDocument(newDocumentXml); 1614 1615 return variantUpdatedDocument; 1616 } 1617 1618 private DocumentVariantCreatedDocument createNewVariantEvent(DocumentImpl document, long documentId, Date lastModified, String summary, long newVariantUpdateCount, long newDocumentUpdateCount) throws RepositoryException { 1619 DocumentImpl.IntimateAccess documentInt = document.getIntimateAccess(this); 1620 1621 DocumentVariantCreatedDocument variantCreatedDocument = DocumentVariantCreatedDocument.Factory.newInstance(); 1622 DocumentVariantCreatedDocument.DocumentVariantCreated variantCreated = variantCreatedDocument.addNewDocumentVariantCreated(); 1623 DocumentVariantCreatedDocument.DocumentVariantCreated.NewDocumentVariant newVariant = variantCreated.addNewNewDocumentVariant(); 1624 1625 DocumentDocument.Document documentXml = document.getXml().getDocument(); 1626 updateNewDocumentXml(documentXml, documentId, lastModified, documentInt.getCurrentUser().getId(), newDocumentUpdateCount); 1627 documentXml.setVariantUpdateCount(newVariantUpdateCount); 1630 documentXml.setVariantLastModified(getCalendar(lastModified)); 1631 documentXml.setVariantLastModifier(documentInt.getCurrentUser().getId()); 1632 documentXml.setSummary(summary); 1633 newVariant.setDocument(documentXml); 1634 1635 return variantCreatedDocument; 1636 } 1637 1638 public void deleteDocument(long documentId, AuthenticatedUser user) throws RepositoryException { 1639 PreparedStatement stmt = null; 1640 Connection conn = null; 1641 List executeAfterCommit = new ArrayList(); 1642 Sync avoidSuspendLock = context.getBlobStore().getAvoidSuspendLock(); 1643 try { 1644 if (!avoidSuspendLock.attempt(0)) 1645 throw new RepositoryException("The blobstore is currently protected for write access, it is impossible to delete documents right now. Try again later."); 1646 } catch (InterruptedException e) { 1647 throw new RuntimeException (e); 1648 } 1649 try { 1650 conn = context.getDataSource().getConnection(); 1651 jdbcHelper.startTransaction(conn); 1652 1653 stmt = conn.prepareStatement("select id from documents where id = ? " + jdbcHelper.getSharedLockClause()); 1655 stmt.setLong(1, documentId); 1656 ResultSet rs = stmt.executeQuery(); 1657 if (!rs.next()) { 1658 throw new DocumentNotFoundException(documentId); 1659 } 1660 stmt.close(); 1661 1662 AvailableVariant[] availableVariants = getAvailableVariantsInTransaction(documentId, user, conn, true); 1664 for (int i = 0; i < availableVariants.length; i++) { 1665 AvailableVariant availableVariant = availableVariants[i]; 1666 AclResultInfo aclInfo = context.getCommonRepository().getAccessManager().getAclInfoOnLive(systemUser, 1668 user.getId(), user.getActiveRoleIds(), documentId, availableVariant.getBranchId(), availableVariant.getLanguageId()); 1669 if (!aclInfo.isAllowed(AclPermission.DELETE)) 1670 throw new AccessException("User " + user.getId() + " (" + user.getLogin() + ") is not allowed to delete document ID " + documentId + ", since the user has no delete permission for document ID " + documentId + ", branch " + getBranchLabel(availableVariant.getBranchId()) + ", language " + getLanguageLabel(availableVariant.getLanguageId())); 1671 1672 LockInfo lockInfo = loadLock(documentId, availableVariant.getBranchId(), availableVariant.getLanguageId(), conn, true, executeAfterCommit); 1674 if (lockInfo.hasLock() && lockInfo.getType() == LockType.PESSIMISTIC 1675 && lockInfo.getUserId() != user.getId()) { 1676 throw new RepositoryException("Cannot delete document " + documentId + " because someone else is holding a pessimistic lock on it: user " + lockInfo.getUserId() + " on branch " + getBranchLabel(availableVariant.getBranchId()) + ", language " + getLanguageLabel(availableVariant.getLanguageId())); 1677 } 1678 } 1679 1680 DocumentImpl deletedDocument = loadDocumentInTransaction(user, documentId, -1, -1, conn, executeAfterCommit); 1683 1684 HashSet blobIds = new HashSet(); 1686 for (int i = 0; i < availableVariants.length; i++) { 1687 AvailableVariant availableVariant = availableVariants[i]; 1688 deleteVariantInTransaction(documentId, availableVariant.getBranchId(), availableVariant.getLanguageId(), blobIds, user, conn, executeAfterCommit); 1689 } 1690 1691 stmt = conn.prepareStatement("delete from documents where id = ?"); 1693 stmt.setLong(1, documentId); 1694 stmt.execute(); 1695 stmt.close(); 1696 1697 XmlObject eventDescription = createDocumentDeletedEvent(deletedDocument, user); 1699 eventHelper.createEvent(eventDescription, "DocumentDeleted", conn); 1700 1701 conn.commit(); 1703 1704 deleteBlobs(blobIds, conn); 1706 } catch (Throwable e) { 1707 jdbcHelper.rollback(conn); 1708 executeAfterCommit.clear(); 1709 if (e instanceof AccessException) 1710 throw (AccessException)e; 1711 if (e instanceof DocumentNotFoundException) 1712 throw (DocumentNotFoundException)e; 1713 1714 throw new RepositoryException("Error deleting document " + documentId + ".", e); 1715 } finally { 1716 jdbcHelper.closeStatement(stmt); 1717 jdbcHelper.closeConnection(conn); 1718 avoidSuspendLock.release(); 1719 executeRunnables(executeAfterCommit); 1720 } 1721 1722 context.getCommonRepository().fireRepositoryEvent(RepositoryEventType.DOCUMENT_DELETED, documentId, -1); 1724 } 1725 1726 private void deleteBlobs(Set blobIds, Connection conn) throws SQLException { 1727 if (blobIds.size() > 0) { 1728 PreparedStatement stmt = null; 1729 try { 1730 stmt = conn.prepareStatement("select count(*) from parts where blob_id = ?"); 1731 Iterator blobIdsIt = blobIds.iterator(); 1732 while (blobIdsIt.hasNext()) { 1733 String blobId = (String )blobIdsIt.next(); 1734 stmt.setString(1, blobId); 1735 ResultSet rs = stmt.executeQuery(); 1736 rs.next(); 1737 if (rs.getLong(1) == 0) { 1738 try { 1739 context.getBlobStore().delete(blobId); 1740 } catch (Throwable e) { 1741 logger.error("Error deleting blob with id " + blobId, e); 1742 } 1743 } 1744 } 1745 } finally { 1746 jdbcHelper.closeStatement(stmt); 1747 } 1748 } 1749 } 1750 1751 public void deleteVariant(long documentId, long branchId, long languageId, AuthenticatedUser user) throws RepositoryException { 1752 List executeAfterCommit = new ArrayList(2); 1753 PreparedStatement stmt = null; 1754 Connection conn = null; 1755 Sync avoidSuspendLock = context.getBlobStore().getAvoidSuspendLock(); 1756 try { 1757 if (!avoidSuspendLock.attempt(0)) 1758 throw new RepositoryException("The blobstore is currently protected for write access, it is impossible to delete document variants right now. Try again later."); 1759 } catch (InterruptedException e) { 1760 throw new RuntimeException (e); 1761 } 1762 try { 1763 conn = context.getDataSource().getConnection(); 1764 jdbcHelper.startTransaction(conn); 1765 1766 stmt = conn.prepareStatement("select id from documents where id = ? " + jdbcHelper.getSharedLockClause()); 1768 stmt.setLong(1, documentId); 1769 ResultSet rs = stmt.executeQuery(); 1770 if (!rs.next()) { 1771 throw new DocumentNotFoundException(documentId); 1772 } 1773 stmt.close(); 1774 1775 AclResultInfo aclInfo = context.getCommonRepository().getAccessManager().getAclInfoOnLive(systemUser, 1777 user.getId(), user.getActiveRoleIds(), documentId, branchId, languageId); 1778 if (!aclInfo.isAllowed(AclPermission.DELETE)) 1779 throw new AccessException("User " + user.getId() + " (" + user.getLogin() + ") is not allowed to delete the variant: document ID " + documentId + ", branch " + getBranchLabel(branchId) + ", language " + getLanguageLabel(languageId)); 1780 1781 stmt = conn.prepareStatement("select count(*) from document_variants where doc_id = ?"); 1783 stmt.setLong(1, documentId); 1784 rs = stmt.executeQuery(); 1785 rs.next(); 1786 long count = rs.getLong(1); 1787 if (count <= 1) 1788 throw new RepositoryException("Deleting the last variant of a document is not possible."); 1789 1790 HashSet blobIds = new HashSet(); 1791 deleteVariantInTransaction(documentId, branchId, languageId, blobIds, user, conn, executeAfterCommit); 1792 1793 conn.commit(); 1795 1796 deleteBlobs(blobIds, conn); 1798 } catch (Throwable e) { 1799 jdbcHelper.rollback(conn); 1800 executeAfterCommit.clear(); 1801 if (e instanceof AccessException) 1802 throw (AccessException)e; 1803 if (e instanceof DocumentNotFoundException) 1804 throw (DocumentNotFoundException)e; 1805 if (e instanceof DocumentVariantNotFoundException) 1806 throw (DocumentVariantNotFoundException)e; 1807 1808 throw new RepositoryException("Error deleting variant: document ID " + documentId + ", branch " + getBranchLabel(branchId) + ", language " + getLanguageLabel(languageId), e); 1809 } finally { 1810 jdbcHelper.closeStatement(stmt); 1811 jdbcHelper.closeConnection(conn); 1812 avoidSuspendLock.release(); 1813 executeRunnables(executeAfterCommit); 1814 } 1815 } 1816 1817 private void executeRunnables(List runnables) { 1818 while (runnables.size() > 0) { 1819 Runnable runnable = (Runnable )runnables.remove(runnables.size() - 1); 1820 runnable.run(); 1821 } 1822 } 1823 1824 1829 private void deleteVariantInTransaction(final long documentId, final long branchId, final long languageId, Set blobIds, 1830 AuthenticatedUser user, Connection conn, List executeAfterCommit) throws SQLException, RepositoryException { 1831 PreparedStatement stmt = null; 1832 try { 1833 DocumentImpl deletedDocument = loadDocumentInTransaction(user, documentId, branchId, languageId, conn, executeAfterCommit); 1834 1835 stmt = conn.prepareStatement("delete from document_variants where doc_id = ? and branch_id = ? and lang_id = ?"); 1837 stmt.setLong(1, documentId); 1838 stmt.setLong(2, branchId); 1839 stmt.setLong(3, languageId); 1840 stmt.execute(); 1841 stmt.close(); 1842 1843 stmt = conn.prepareStatement("delete from locks where doc_id = ? and branch_id = ? and lang_id = ?"); 1845 stmt.setLong(1, documentId); 1846 stmt.setLong(2, branchId); 1847 stmt.setLong(3, languageId); 1848 stmt.execute(); 1849 stmt.close(); 1850 1851 stmt = conn.prepareStatement("delete from summaries where doc_id = ? and branch_id = ? and lang_id = ?"); 1853 stmt.setLong(1, documentId); 1854 stmt.setLong(2, branchId); 1855 stmt.setLong(3, languageId); 1856 stmt.execute(); 1857 stmt.close(); 1858 1859 stmt = conn.prepareStatement("delete from document_collections where document_id = ? and branch_id = ? and lang_id = ?"); 1861 stmt.setLong(1, documentId); 1862 stmt.setLong(2, branchId); 1863 stmt.setLong(3, languageId); 1864 stmt.execute(); 1865 stmt.close(); 1866 1867 stmt = conn.prepareStatement("delete from customfields where doc_id = ? and branch_id = ? and lang_id = ?"); 1869 stmt.setLong(1, documentId); 1870 stmt.setLong(2, branchId); 1871 stmt.setLong(3, languageId); 1872 stmt.execute(); 1873 stmt.close(); 1874 1875 stmt = conn.prepareStatement("delete from extracted_links where source_doc_id = ? and source_branch_id = ? and source_lang_id = ?"); 1877 stmt.setLong(1, documentId); 1878 stmt.setLong(2, branchId); 1879 stmt.setLong(3, languageId); 1880 stmt.execute(); 1881 stmt.close(); 1882 1883 stmt = conn.prepareStatement("delete from comments where doc_id = ? and branch_id = ? and lang_id = ?"); 1885 stmt.setLong(1, documentId); 1886 stmt.setLong(2, branchId); 1887 stmt.setLong(3, languageId); 1888 stmt.execute(); 1889 stmt.close(); 1890 1891 stmt = conn.prepareStatement("select distinct(blob_id) from parts where doc_id = ? and branch_id = ? and lang_id = ?"); 1893 stmt.setLong(1, documentId); 1894 stmt.setLong(2, branchId); 1895 stmt.setLong(3, languageId); 1896 ResultSet rs = stmt.executeQuery(); 1897 while (rs.next()) 1898 blobIds.add(rs.getString(1)); 1899 stmt.close(); 1900 1901 stmt = conn.prepareStatement("delete from parts where doc_id = ? and branch_id = ? and lang_id = ?"); 1903 stmt.setLong(1, documentId); 1904 stmt.setLong(2, branchId); 1905 stmt.setLong(3, languageId); 1906 stmt.execute(); 1907 stmt.close(); 1908 1909 stmt = conn.prepareStatement("delete from thefields where doc_id = ? and branch_id = ? and lang_id = ?"); 1911 stmt.setLong(1, documentId); 1912 stmt.setLong(2, branchId); 1913 stmt.setLong(3, languageId); 1914 stmt.execute(); 1915 stmt.close(); 1916 1917 stmt = conn.prepareStatement("delete from links where doc_id = ? and branch_id = ? and lang_id = ?"); 1919 stmt.setLong(1, documentId); 1920 stmt.setLong(2, branchId); 1921 stmt.setLong(3, languageId); 1922 stmt.execute(); 1923 stmt.close(); 1924 1925 stmt = conn.prepareStatement("delete from document_versions where doc_id = ? and branch_id = ? and lang_id = ?"); 1927 stmt.setLong(1, documentId); 1928 stmt.setLong(2, branchId); 1929 stmt.setLong(3, languageId); 1930 stmt.execute(); 1931 stmt.close(); 1932 1933 XmlObject eventDescription = createVariantDeletedEvent(deletedDocument, user); 1934 eventHelper.createEvent(eventDescription, "DocumentVariantDeleted", conn); 1935 1936 executeAfterCommit.add(new Runnable () { 1937 public void run() { 1938 context.getCommonRepository().fireVariantEvent(DocumentVariantEventType.DOCUMENT_VARIANT_DELETED, documentId, branchId, languageId, -1); 1939 } 1940 }); 1941 1942 } finally { 1943 jdbcHelper.closeStatement(stmt); 1944 } 1945 } 1946 1947 private DocumentVariantDeletedDocument createVariantDeletedEvent(DocumentImpl document, AuthenticatedUser user) throws RepositoryException { 1948 DocumentVariantDeletedDocument variantDeletedDocument = DocumentVariantDeletedDocument.Factory.newInstance(); 1949 DocumentVariantDeletedDocument.DocumentVariantDeleted variantDeleted = variantDeletedDocument.addNewDocumentVariantDeleted(); 1950 variantDeleted.addNewDeletedDocumentVariant().setDocument(document.getXml().getDocument()); 1951 variantDeleted.setDeletedTime(new GregorianCalendar()); 1952 variantDeleted.setDeleterId(user.getId()); 1953 return variantDeletedDocument; 1954 } 1955 1956 private DocumentDeletedDocument createDocumentDeletedEvent(DocumentImpl document, AuthenticatedUser user) { 1957 DocumentDeletedDocument documentDeletedDocument = DocumentDeletedDocument.Factory.newInstance(); 1958 DocumentDeletedDocument.DocumentDeleted documentDeleted = documentDeletedDocument.addNewDocumentDeleted(); 1959 documentDeleted.addNewDeletedDocument().setDocument(document.getXmlWithoutVariant().getDocument()); 1960 documentDeleted.setDeletedTime(new GregorianCalendar()); 1961 documentDeleted.setDeleterId(user.getId()); 1962 return documentDeletedDocument; 1963 } 1964 1965 public AvailableVariantImpl[] getAvailableVariants(long documentId, AuthenticatedUser user) throws RepositoryException { 1966 Connection conn = null; 1967 try { 1968 conn = context.getDataSource().getConnection(); 1969 return getAvailableVariantsInTransaction(documentId, user, conn, false); 1970 } catch (Throwable e) { 1971 throw new RepositoryException("Error getting variants of document " + documentId + ".", e); 1972 } finally { 1973 jdbcHelper.closeConnection(conn); 1974 } 1975 } 1976 1977 public AvailableVariantImpl[] getAvailableVariantsInTransaction(long documentId, AuthenticatedUser user, Connection conn, boolean takeLock) throws RepositoryException, SQLException { 1978 PreparedStatement stmt = null; 1979 try { 1980 stmt = conn.prepareStatement("select branch_id, lang_id, retired, liveversion_id from document_variants where doc_id = ? " + (takeLock ? jdbcHelper.getSharedLockClause() : "")); 1981 stmt.setLong(1, documentId); 1982 ResultSet rs = stmt.executeQuery(); 1983 1984 if (!rs.next()) 1985 throw new DocumentNotFoundException(documentId); 1986 1987 List availableVariants = new ArrayList(); 1988 do { 1989 long branchId = rs.getLong("branch_id"); 1990 long languageId = rs.getLong("lang_id"); 1991 boolean retired = rs.getBoolean("retired"); 1992 long liveVersionId = rs.getLong("liveversion_id"); 1993 1994 AvailableVariantImpl availableVariant = new AvailableVariantImpl(branchId, languageId, retired, liveVersionId, context.getCommonRepository().getVariantManager(), user); 1995 availableVariants.add(availableVariant); 1996 } while (rs.next()); 1997 1998 return (AvailableVariantImpl[])availableVariants.toArray(new AvailableVariantImpl[availableVariants.size()]); 1999 } finally { 2000 jdbcHelper.closeStatement(stmt); 2001 } 2002 } 2003 2004 public Document createVariant(long documentId, long startBranchId, long startLanguageId, long startVersionId, 2005 long newBranchId, long newLanguageId, AuthenticatedUser user) throws RepositoryException { 2006 try { 2007 Document startDocument = load(user, documentId, startBranchId, startLanguageId); 2008 VersionImpl startVersion; 2009 if (startVersionId == -1) { 2010 startVersion = (VersionImpl)startDocument.getLastVersion(); 2011 } else if (startVersionId == -2) { 2012 startVersion = (VersionImpl)startDocument.getLiveVersion(); 2013 if (startVersion == null) 2014 throw new RepositoryException("Requested to create a variant starting from the live version, but the document does not have a live version. Document: " + new VariantKey(documentId, startBranchId, startLanguageId)); 2015 } else { 2016 startVersion = (VersionImpl)startDocument.getVersion(startVersionId); 2017 } 2018 2019 DocumentImpl newDocument = new DocumentImpl(this, context.getCommonRepository(), user, startDocument.getDocumentTypeId(), newBranchId, newLanguageId); 2020 DocumentImpl.IntimateAccess newDocumentInt = newDocument.getIntimateAccess(this); 2021 DocumentVariantImpl newVariant = newDocumentInt.getVariant(); 2022 DocumentVariantImpl.IntimateAccess newVariantInt = newVariant.getIntimateAccess(this); 2023 2024 newVariantInt.setCreatedFrom(startBranchId, startLanguageId, startVersion.getId()); 2025 2026 newDocumentInt.load(startDocument.getId(), startDocument.getLastModified(), startDocument.getLastModifier(), 2027 startDocument.getCreated(), startDocument.getOwner(), startDocument.isPrivate(), startDocument.getUpdateCount()); 2028 2029 newDocument.setRetired(startDocument.isRetired()); 2030 2031 newDocument.setName(startDocument.getName()); 2033 2034 PartImpl[] parts = startVersion.getIntimateAccess(this).getPartImpls(); 2036 for (int i = 0; i < parts.length; i++) { 2037 newVariantInt.addPart(parts[i].getIntimateAccess(this).internalDuplicate(newVariantInt)); 2038 } 2039 2040 Field[] fields = startVersion.getFields().getArray(); 2042 for (int i = 0; i < fields.length; i++) { 2043 newDocument.setField(fields[i].getTypeId(), fields[i].getValue()); 2044 } 2045 2046 Link[] links = startVersion.getLinks().getArray(); 2048 for (int i = 0; i < links.length; i++) { 2049 newDocument.addLink(links[i].getTitle(), links[i].getTarget()); 2050 } 2051 2052 newDocument.setNewVersionState(startVersion.getState()); 2053 2054 Map customFields = startDocument.getCustomFields(); 2056 for (Iterator customFieldsIt = customFields.entrySet().iterator(); customFieldsIt.hasNext(); ) { 2057 Map.Entry customField = (Map.Entry)customFieldsIt.next(); 2058 String name = (String )customField.getKey(); 2059 String value = (String )customField.getValue(); 2060 newDocument.setCustomField(name, value); 2061 } 2062 2063 DocumentCollection[] collections = startDocument.getCollections().getArray(); 2065 for (int i = 0; i < collections.length; i++) { 2066 newDocument.addToCollection(collections[i]); 2067 } 2068 2069 newDocument.save(false); 2070 2071 return newDocument; 2072 } catch (Exception e) { 2073 throw new RepositoryException("Error while creating new variant based on existing variant.", e); 2074 } 2075 } 2076 2077 public String getClientVersion(AuthenticatedUser user) { 2078 return getServerVersion(user); 2080 } 2081 2082 public String getServerVersion(AuthenticatedUser user) { 2083 Properties versionProps; 2084 try { 2085 versionProps = VersionHelper.getVersionProperties(getClass().getClassLoader(), "org/outerj/daisy/repository/serverimpl/versioninfo.properties"); 2086 } catch (IOException e) { 2087 throw new RepositoryRuntimeException("Error getting version information.", e); 2088 } 2089 String version = VersionHelper.getVersion(versionProps); 2090 if (version != null) 2091 return version; 2092 else 2093 throw new RepositoryRuntimeException("Version unknown."); 2094 } 2095} 2096 | Popular Tags |