KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > outerj > daisy > repository > serverimpl > LocalDocumentStrategy


1 /*
2  * Copyright 2004 Outerthought bvba and Schaubroeck nv
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

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 JavaDoc;
38 import java.math.BigDecimal JavaDoc;
39 import java.io.InputStream JavaDoc;
40 import java.io.IOException JavaDoc;
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         // check here that branchId and languageId are not -1, since the loadDocumentInTransaction actually
52
// allows them to be -1 to avoid the variant to be loaded, but we don't want to make this functionality public
53
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 JavaDoc 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     /**
90      * Loads a document inside an already started connection / transaction.
91      * It also DOES NOT check access rights.
92      *
93      * If either the branchId or languageId is -1, a document object without loaded variant will be returned.
94      * This is for internal use only.
95      */

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 JavaDoc 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 JavaDoc 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 JavaDoc(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 JavaDoc[] 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 JavaDoc 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 JavaDoc[] 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 JavaDoc 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         // Check if the user has write access to the document variant
369
if (!variant.isNew()) {
370             // Note that here we retrieve the ACL info using the document ID,
371
// thus giving the ACL info before document modifications. This is necessary
372
// because otherwise someone could modify field values to get write access
373
// to the document, even if before they didn't have such access.
374
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         // Check that the author can't save or create a document to which he himself has no access
382
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         // Execute pre-save hooks
392
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 JavaDoc e) {
401                     logger.error("Error executing pre-save-hook " + hook.getName() + " on document " + document.getVariantKey(), e);
402                 }
403             }
404         }
405
406         // Check new version state
407
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         // start database stuff
417
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             // load the old document (if any), needed to generate event later on
425
// this is our last chance to do this
426
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 JavaDoc now = new Date JavaDoc();
434
435             //
436
// PART 1: Store the document itself
437
//
438

439             // Note: documentNewUpdateCount should always contain the correct update count of the document, even if
440
// the document itself is not modified, because we need this further on
441
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()); // same as owner of course
454
stmt.setLong(7, documentNewUpdateCount);
455
456                 stmt.execute();
457                 stmt.close();
458
459                 // create event record
460
XmlObject eventDescription = createNewDocumentEvent(document, id, now, 1L);
461                 eventHelper.createEvent(eventDescription, "DocumentCreated", conn);
462             } else if (document.needsSaving()) {
463                 // check the document is really there and not modified by someone else
464
// use lock clause to avoid others updating the document while we're at it.
465
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                     // Note that even if a user has an exclusive editing lock, this lock doesn't apply to the
472
// document record and thus it is possible that saving will fail. However, in most frontend
473
// applications the editing of a variant should be separate from the editing of shared document
474
// properties, so that a user will not run into the aforementioned situation.
475
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                 // update document record
485
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                 // create event record
496
XmlObject eventDescription = createDocumentUpdatedEvent(oldDocument, document, now, documentNewUpdateCount);
497                 eventHelper.createEvent(eventDescription, "DocumentUpdated", conn);
498
499                 // free document record for others to update (so that they don't have to wait until the variant is
500
// stored too.
501
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             //
511
// PART 2: Store the document variant
512
//
513

514
515             // check that if there is a pessimistic lock on the document variant, it is owned by the
516
// current user. Also make that the lock record is selected with a lock so that
517
// no one can create a lock while the document variant is being saved.
518
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                 // check the document variant is really there and not modified by someone else
546
// use lock clause to avoid others updating the document while we're at it.
547
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 JavaDoc summary = null;
568             long variantNewUpdateCount = -1;
569             if (newVersion) {
570
571                 // store the various changes
572
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                 // update document_variants record
595
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                 // extract links
610
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                 // create summary
615
if (summaryNeedsUpdating) {
616                     summary = storeSummary(conn, document, id, -1);
617                 } else {
618                     summary = document.getSummary();
619                 }
620
621
622                 // store event record (this is used for guaranteed, assynchronous events)
623
// This is done as part of the transaction to be sure that it is not lost: either
624
// the document variant and the event record are stored, or both are not.
625
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             // now that everything's saved correctly, update document object status
639
if (newVersion)
640                 variantInt.saved(lastVersionId, liveVersionId, now, summary, variantNewUpdateCount);
641             if (isDocumentNew)
642                 documentInt.saved(id, now, now, documentNewUpdateCount);
643         } catch (Throwable JavaDoc 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         // fire synchronous events
658
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     /**
708      *
709      * @param versionId live version id, or -1 if the live-version-to-be is the data in the document object
710      */

711     private String JavaDoc storeSummary(Connection conn, DocumentImpl document, long documentId, long versionId) throws SQLException {
712         PreparedStatement stmt = null;
713         try {
714             String JavaDoc 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 JavaDoc 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         // first we need to remove this document variant from all the collections it belongs to
755
clearCollections(id, variant.getBranchId(), variant.getLanguageId(), conn);
756         // now we can add the document variant to its updated set of collections
757
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 JavaDoc now, Connection conn) throws SQLException {
776         PreparedStatement stmt = null;
777         try {
778             long newVersionId = 1;
779
780             if (document.getId() != -1) {
781                 // get the number of the previous last version
782
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 JavaDoc)entry.getKey());
841                 stmt.setString(5, (String JavaDoc)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 JavaDoc oldBlobKey = partInt.getBlobKey();
857                 String JavaDoc blobKey = partInt.getBlobKey();
858                 if (partInt.isDataUpdated()) {
859                     // first store the blob
860
try {
861                         PartDataSource partDataSource = partInt.getPartDataSource();
862                         blobKey = context.getBlobStore().store(partDataSource.createInputStream());
863                     } catch (Exception JavaDoc e) {
864                         throw new RepositoryException("Error storing part data to blobstore.", e);
865                     }
866                     partUpdates.add(new PartUpdate(partInt, blobKey, oldBlobKey));
867                     // we update the object model here already, even though the transaction is not yet
868
// committed, so that other components like the link extractor and the summarizer can
869
// read out the new data. This change will be rolled back if the transaction changes
870
// (see PartUpdate.rollback())
871
partInt.setBlobKey(blobKey);
872                 }
873
874                 // insert a record
875
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 JavaDoc blobKey;
899         private final String JavaDoc oldBlobKey;
900         private final PartDataSource partDataSource;
901
902         public PartUpdate(PartImpl.IntimateAccess partInt, String JavaDoc blobKey, String JavaDoc 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 JavaDoc 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             // insert new records
925
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 JavaDoc[] values = fields[i].isMultiValue() ? (Object JavaDoc[])fields[i].getValue() : new Object JavaDoc[] {fields[i].getValue()};
936
937                 stmt.setLong(7, values.length);
938                 for (int k = 0; k < values.length; k++) {
939                     Object JavaDoc value = values[k];
940                     String JavaDoc stringValue = (String JavaDoc)(fieldType == ValueType.STRING ? value : null);
941                     Date JavaDoc dateValue = (fieldType == ValueType.DATE ? new java.sql.Date JavaDoc(((Date JavaDoc)value).getTime()) : null);
942                     Timestamp datetimeValue = (fieldType == ValueType.DATETIME ? new Timestamp(((Date JavaDoc)value).getTime()) : null);
943                     Long JavaDoc integerValue = (Long JavaDoc)(fieldType == ValueType.LONG ? value : null);
944                     Double JavaDoc floatValue = (Double JavaDoc)(fieldType == ValueType.DOUBLE ? value : null);
945                     BigDecimal JavaDoc decimalValue = (BigDecimal JavaDoc)(fieldType == ValueType.DECIMAL ? value : null);
946                     Boolean JavaDoc booleanValue = (Boolean JavaDoc)(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 JavaDoc 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 JavaDoc getBlob(String JavaDoc 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 JavaDoc createdOn = rs.getTimestamp("created_on");
1039                long createdBy = rs.getLong("created_by");
1040                String JavaDoc documentName = rs.getString("name");
1041                long versionId = rs.getLong("id");
1042                VersionState versionState = VersionState.getByCode(rs.getString("state"));
1043                Date JavaDoc 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 JavaDoc 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 JavaDoc createdOn = rs.getTimestamp("created_on");
1078            long createdBy = rs.getLong("created_by");
1079            String JavaDoc documentName = rs.getString("name");
1080            VersionState versionState = VersionState.getByCode(rs.getString("state"));
1081            Date JavaDoc 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 JavaDoc 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 JavaDoc 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            // check ACL.
1129
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            // lock the document variant while we're doing this
1143
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 JavaDoc lastModified = new Date JavaDoc();
1151            long lastModifier = documentInt.getCurrentUser().getId();
1152            // update document_versions record
1153
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            // update live version id in document variant record
1167
// but first retrieve previous value to embed in change event
1168
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            // update extracted links and summary text if live version changed
1189
String JavaDoc 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            // insert an event record in the events table describing the change we've done
1199
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 JavaDoc 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        // Check if user has write access
1252
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            // lock the database record for the document variant so that the document variant cannot be saved while
1267
// a lock is being taken
1268
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            // first check if there is already a lock on this document variant
1276
lockInfo = loadLock(variant.getDocumentId(), variant.getBranchId(), variant.getLanguageId(), conn, true, executeAfterCommit);
1277
1278            // if there is a pessimistic lock belonging to another user, we cannot override it.
1279
// In that case information about the current lock is returned.
1280
if (lockInfo.hasLock() && lockInfo.getType() == LockType.PESSIMISTIC
1281                    && lockInfo.getUserId() != variantInt.getCurrentUser().getId()) {
1282                return lockInfo;
1283            }
1284
1285            if (lockInfo.hasLock()) {
1286                // delete current lock record
1287
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            // insert the new lock
1296
lockInfo = new LockInfoImpl(variantInt.getCurrentUser().getId(), new Date JavaDoc(), 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 JavaDoc 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        // fire synchronous events
1322
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 JavaDoc e) {
1338            throw new RepositoryException("Error loading lock info.", e);
1339        } finally {
1340            jdbcHelper.closeConnection(conn);
1341        }
1342    }
1343
1344    /**
1345     * Loads current lock info for a document variant. If the lock has meanwhile expired, it is
1346     * removed by this loading operation.
1347     *
1348     * <p>This removal however needs to be notified via an event, since otherwise the cache
1349     * will not get properly invalidated. However, this event should only be sent if and
1350     * especially after the connection has been committed (otherwise the cache gets invalidated,
1351     * and it might get refilled with old info before the commit). Therefore, the caller should
1352     * execute any Runnables added to the executeAfterCommit list after commit of the running transaction.
1353     * (Note: pay attention to return statements in the middle of methods).
1354     *
1355     * <p>Optionally this method can select the lock record with a shared mode lock, so
1356     * that you can be sure nobody else is updating the lockinfo while you're working with it.
1357     */

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 JavaDoc timeAcquired = rs.getTimestamp("time_acquired");
1371            long duration = rs.getLong("duration");
1372            stmt.close();
1373
1374            // check if the lock has meanwhile expired, and if so delete it
1375
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                // Note that we pass all lock information in the statement, not only the doc_id,
1379
// to be sure that we're deleting the record only in case nobody else has modified
1380
// it in the meantime
1381
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 JavaDoc() {
1393                    public void run() {
1394                        // fire synchronous events
1395
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        // Check if user has write access
1410
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            // fire synchronous events
1440
context.getCommonRepository().fireVariantEvent(DocumentVariantEventType.LOCK_CHANGE, variant.getDocumentId(), variant.getBranchId(), variant.getLanguageId(), -1);
1441
1442            return new LockInfoImpl();
1443        } catch (Throwable JavaDoc 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    /**
1456     * Loads all the collections to which a specified document belongs.
1457     * @param variant the document variant we want to load the collections for
1458     * @return a java.util.Collection the document belongs to, null if no collections can be found
1459     * (which conceptually means the document only belongs to the <i>root collection</i>.
1460     */

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                        // ignore: this would mean the collection has been deleted since the transaction
1489
// that loads this document started
1490
}
1491                    anotherRecordFound = rs.next();
1492                }
1493            }
1494
1495        } catch (Throwable JavaDoc 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                    /*
1522                     * This exception can mean a couple of things.
1523                     * First: it can be a genuine datastore exception.
1524                     * if this is the case, we can rethrow it as such.
1525                     *
1526                     * Second: it is possible that an admin has removed
1527                     * a collection and at the same time, someone was
1528                     * editing a document. If this document belonged
1529                     * to the removed collection, this will result in an
1530                     * exception due to the constraint on the database.
1531                     *
1532                     * In this case we want to throw a specific exception.
1533                     */

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                        //we're not actually interested in e1, so we just rethrow the previous exception
1540
//because it was not caused by collection deletion.
1541
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 JavaDoc 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    /**
1565     * Updates the document XML so as if it would look if we retrieved the XML of the document after saving it.
1566     */

1567    private void updateUpdatedDocumentXml(DocumentDocument.Document documentXml, Date JavaDoc 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 JavaDoc 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    /**
1588     * Updates the document XML so as if it would look if we retrieved the XML of the document after saving it.
1589     */

1590    private void updateNewDocumentXml(DocumentDocument.Document documentXml, long documentId, Date JavaDoc 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 JavaDoc lastModified, String JavaDoc 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        // update the XML so as if it would look if we retrieved the XML after the internal document object state
1607
// is already modified
1608
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 JavaDoc lastModified, String JavaDoc 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        // update the XML so as if it would look if we retrieved the XML after the internal document object state
1628
// is already modified
1629
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 JavaDoc e) {
1647            throw new RuntimeException JavaDoc(e);
1648        }
1649        try {
1650            conn = context.getDataSource().getConnection();
1651            jdbcHelper.startTransaction(conn);
1652
1653            // take a write lock on the document record
1654
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            // for each variant, check the access permissions and the presence of an exclusive lock owned by a different user
1663
AvailableVariant[] availableVariants = getAvailableVariantsInTransaction(documentId, user, conn, true);
1664            for (int i = 0; i < availableVariants.length; i++) {
1665                AvailableVariant availableVariant = availableVariants[i];
1666                // check if the user is allowed to delete the document variant
1667
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                // check there's no lock on the document variant owned by another user
1673
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            // load the old document (if any), needed to generate event later on
1681
// this is our last chance to do this
1682
DocumentImpl deletedDocument = loadDocumentInTransaction(user, documentId, -1, -1, conn, executeAfterCommit);
1683
1684            // delete all variants
1685
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            // delete document itself
1692
stmt = conn.prepareStatement("delete from documents where id = ?");
1693            stmt.setLong(1, documentId);
1694            stmt.execute();
1695            stmt.close();
1696
1697            // make async event
1698
XmlObject eventDescription = createDocumentDeletedEvent(deletedDocument, user);
1699            eventHelper.createEvent(eventDescription, "DocumentDeleted", conn);
1700
1701            // commit document deletion
1702
conn.commit();
1703
1704            // after succesful commit of database transaction, start deleting blobs
1705
deleteBlobs(blobIds, conn);
1706        } catch (Throwable JavaDoc 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        // do sync event
1723
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 JavaDoc blobId = (String JavaDoc)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 JavaDoc 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 JavaDoc e) {
1760            throw new RuntimeException JavaDoc(e);
1761        }
1762        try {
1763            conn = context.getDataSource().getConnection();
1764            jdbcHelper.startTransaction(conn);
1765
1766            // take a write lock on the document record
1767
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            // check if the user is allowed to delete the document variant
1776
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            // check there's still more then one variant
1782
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            // commit document variant deletion
1794
conn.commit();
1795
1796            // after succesful commit of database transaction, start deleting blobs
1797
deleteBlobs(blobIds, conn);
1798        } catch (Throwable JavaDoc 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 JavaDoc runnable = (Runnable JavaDoc)runnables.remove(runnables.size() - 1);
1820            runnable.run();
1821        }
1822    }
1823
1824    /**
1825     * Deletes a document variant, assumes the necessary pre-condition checks are already done
1826     * (ie user is allowed to delete it, there's no pessimistic lock by another user, it's not
1827     * the last variant of the document)
1828     */

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            // delete variant
1836
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            // delete lock
1844
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            // delete summary
1852
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            // delete collection membership
1860
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            // delete custom fields
1868
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            // delete extracted links
1876
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            // delete comments
1884
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            // read all blob ids before deleting the parts
1892
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            // delete parts (of all versions)
1902
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            // delete fields (of all versions)
1910
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            // delete links (of all versions)
1918
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            // delete versions
1926
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 JavaDoc() {
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 JavaDoc 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            // copy document name
2032
newDocument.setName(startDocument.getName());
2033
2034            // copy parts -- efficiently, thus reusing existing blob key
2035
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            // copy fields
2041
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            // copy links
2047
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            // copy custom fields
2055
Map customFields = startDocument.getCustomFields();
2056            for (Iterator customFieldsIt = customFields.entrySet().iterator(); customFieldsIt.hasNext(); ) {
2057                Map.Entry customField = (Map.Entry)customFieldsIt.next();
2058                String JavaDoc name = (String JavaDoc)customField.getKey();
2059                String JavaDoc value = (String JavaDoc)customField.getValue();
2060                newDocument.setCustomField(name, value);
2061            }
2062
2063            // copy collections
2064
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 JavaDoc e) {
2073            throw new RepositoryException("Error while creating new variant based on existing variant.", e);
2074        }
2075    }
2076
2077    public String JavaDoc getClientVersion(AuthenticatedUser user) {
2078        // in the local implementation, client and server version are the same
2079
return getServerVersion(user);
2080    }
2081
2082    public String JavaDoc 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 JavaDoc version = VersionHelper.getVersion(versionProps);
2090        if (version != null)
2091            return version;
2092        else
2093            throw new RepositoryRuntimeException("Version unknown.");
2094    }
2095}
2096
Popular Tags