1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.chemistry.opencmis.client.runtime;
20
21 import static org.apache.chemistry.opencmis.commons.impl.CollectionsHelper.isNotEmpty;
22 import static org.apache.chemistry.opencmis.commons.impl.CollectionsHelper.isNullOrEmpty;
23
24 import java.io.Serializable;
25 import java.util.ArrayList;
26 import java.util.Collections;
27 import java.util.EnumMap;
28 import java.util.EnumSet;
29 import java.util.GregorianCalendar;
30 import java.util.HashMap;
31 import java.util.HashSet;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.Set;
35 import java.util.concurrent.locks.ReentrantReadWriteLock;
36
37 import org.apache.chemistry.opencmis.client.api.CmisObject;
38 import org.apache.chemistry.opencmis.client.api.ObjectFactory;
39 import org.apache.chemistry.opencmis.client.api.ObjectId;
40 import org.apache.chemistry.opencmis.client.api.ObjectType;
41 import org.apache.chemistry.opencmis.client.api.OperationContext;
42 import org.apache.chemistry.opencmis.client.api.Policy;
43 import org.apache.chemistry.opencmis.client.api.Property;
44 import org.apache.chemistry.opencmis.client.api.Relationship;
45 import org.apache.chemistry.opencmis.client.api.Rendition;
46 import org.apache.chemistry.opencmis.client.api.SecondaryType;
47 import org.apache.chemistry.opencmis.commons.PropertyIds;
48 import org.apache.chemistry.opencmis.commons.data.Ace;
49 import org.apache.chemistry.opencmis.commons.data.Acl;
50 import org.apache.chemistry.opencmis.commons.data.AllowableActions;
51 import org.apache.chemistry.opencmis.commons.data.CmisExtensionElement;
52 import org.apache.chemistry.opencmis.commons.data.ObjectData;
53 import org.apache.chemistry.opencmis.commons.data.RenditionData;
54 import org.apache.chemistry.opencmis.commons.definitions.PropertyDefinition;
55 import org.apache.chemistry.opencmis.commons.enums.AclPropagation;
56 import org.apache.chemistry.opencmis.commons.enums.Action;
57 import org.apache.chemistry.opencmis.commons.enums.BaseTypeId;
58 import org.apache.chemistry.opencmis.commons.enums.ExtensionLevel;
59 import org.apache.chemistry.opencmis.commons.enums.Updatability;
60 import org.apache.chemistry.opencmis.commons.exceptions.CmisObjectNotFoundException;
61 import org.apache.chemistry.opencmis.commons.spi.CmisBinding;
62 import org.apache.chemistry.opencmis.commons.spi.Holder;
63
64
65
66
67 public abstract class AbstractCmisObject implements CmisObject, Serializable {
68
69 private static final long serialVersionUID = 1L;
70
71 private SessionImpl session;
72 private ObjectType objectType;
73 private List<SecondaryType> secondaryTypes;
74 private Map<String, Property<?>> properties;
75 private AllowableActions allowableActions;
76 private List<Rendition> renditions;
77 private Acl acl;
78 private List<String> policyIds;
79 private List<Policy> policies;
80 private List<Relationship> relationships;
81 private Map<ExtensionLevel, List<CmisExtensionElement>> extensions;
82 private OperationContext creationContext;
83 private long refreshTimestamp;
84
85 private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
86
87
88
89
90 protected void initialize(SessionImpl session, ObjectType objectType, ObjectData objectData,
91 OperationContext context) {
92 if (session == null) {
93 throw new IllegalArgumentException("Session must be set!");
94 }
95
96 if (objectType == null) {
97 throw new IllegalArgumentException("Object type must be set!");
98 }
99
100 if (objectType.getPropertyDefinitions() == null || objectType.getPropertyDefinitions().size() < 9) {
101
102
103 throw new IllegalArgumentException("Object type must have property definitions!");
104 }
105
106 if (objectData == null) {
107 throw new IllegalArgumentException("Object data must be set!");
108 }
109
110 if (objectData.getProperties() == null) {
111 throw new IllegalArgumentException("Properties must be set!");
112 }
113
114 if (objectData.getId() == null) {
115 throw new IllegalArgumentException("Object ID must be set!");
116 }
117
118 this.session = session;
119 this.objectType = objectType;
120 this.secondaryTypes = null;
121 this.extensions = new EnumMap<ExtensionLevel, List<CmisExtensionElement>>(ExtensionLevel.class);
122 this.creationContext = new OperationContextImpl(context);
123 this.refreshTimestamp = System.currentTimeMillis();
124
125 ObjectFactory of = getObjectFactory();
126
127
128 if (objectData.getProperties().getProperties() != null
129 && objectData.getProperties().getProperties().containsKey(PropertyIds.SECONDARY_OBJECT_TYPE_IDS)) {
130 @SuppressWarnings("unchecked")
131 List<String> stids = (List<String>) objectData.getProperties().getProperties()
132 .get(PropertyIds.SECONDARY_OBJECT_TYPE_IDS).getValues();
133 if (isNotEmpty(stids)) {
134 secondaryTypes = new ArrayList<SecondaryType>(stids.size());
135 for (String stid : stids) {
136 if (stid != null) {
137 ObjectType type = session.getTypeDefinition(stid);
138 if (type instanceof SecondaryType) {
139 secondaryTypes.add((SecondaryType) type);
140 }
141 }
142 }
143 } else {
144 secondaryTypes = null;
145 }
146 }
147
148
149 this.properties = of.convertProperties(objectType, secondaryTypes, objectData.getProperties());
150 extensions.put(ExtensionLevel.PROPERTIES, objectData.getProperties().getExtensions());
151
152
153 if (objectData.getAllowableActions() != null) {
154 allowableActions = objectData.getAllowableActions();
155 extensions.put(ExtensionLevel.ALLOWABLE_ACTIONS, objectData.getAllowableActions().getExtensions());
156 } else {
157 allowableActions = null;
158 }
159
160
161 if (objectData.getRenditions() != null && !objectData.getRenditions().isEmpty()) {
162 renditions = new ArrayList<Rendition>(objectData.getRenditions().size());
163 for (RenditionData rd : objectData.getRenditions()) {
164 renditions.add(of.convertRendition(getId(), rd));
165 }
166 } else {
167 renditions = null;
168 }
169
170
171 if (objectData.getAcl() != null) {
172 acl = objectData.getAcl();
173 extensions.put(ExtensionLevel.ACL, objectData.getAcl().getExtensions());
174
175 if (objectData.isExactAcl() != null) {
176 final Acl objectAcl = objectData.getAcl();
177 final Boolean isExact = objectData.isExactAcl();
178 acl = new Acl() {
179
180 @Override
181 public void setExtensions(List<CmisExtensionElement> extensions) {
182 objectAcl.setExtensions(extensions);
183 }
184
185 @Override
186 public List<CmisExtensionElement> getExtensions() {
187 return objectAcl.getExtensions();
188 }
189
190 @Override
191 public Boolean isExact() {
192 return isExact;
193 }
194
195 @Override
196 public List<Ace> getAces() {
197 return objectAcl.getAces();
198 }
199 };
200 }
201 } else {
202 acl = null;
203 }
204
205
206 policies = null;
207 if (objectData.getPolicyIds() != null && objectData.getPolicyIds().getPolicyIds() != null) {
208 if (objectData.getPolicyIds().getPolicyIds().isEmpty()) {
209 policyIds = null;
210 } else {
211 policyIds = objectData.getPolicyIds().getPolicyIds();
212 }
213 extensions.put(ExtensionLevel.POLICIES, objectData.getPolicyIds().getExtensions());
214 } else {
215 policyIds = null;
216 }
217
218
219 if (objectData.getRelationships() != null && !objectData.getRelationships().isEmpty()) {
220 relationships = new ArrayList<Relationship>(objectData.getRelationships().size());
221 for (ObjectData rod : objectData.getRelationships()) {
222 CmisObject relationship = of.convertObject(rod, this.creationContext);
223 if (relationship instanceof Relationship) {
224 relationships.add((Relationship) relationship);
225 }
226 }
227 } else {
228 relationships = null;
229 }
230
231 extensions.put(ExtensionLevel.OBJECT, objectData.getExtensions());
232 }
233
234
235
236
237 protected void writeLock() {
238 lock.writeLock().lock();
239 }
240
241
242
243
244 protected void writeUnlock() {
245 lock.writeLock().unlock();
246 }
247
248
249
250
251 protected void readLock() {
252 lock.readLock().lock();
253 }
254
255
256
257
258 protected void readUnlock() {
259 lock.readLock().unlock();
260 }
261
262
263
264
265 protected SessionImpl getSession() {
266 return session;
267 }
268
269
270
271
272 protected String getRepositoryId() {
273 return getSession().getRepositoryId();
274 }
275
276
277
278
279 protected ObjectType getObjectType() {
280 readLock();
281 try {
282 return objectType;
283 } finally {
284 readUnlock();
285 }
286 }
287
288
289
290
291 protected CmisBinding getBinding() {
292 return getSession().getBinding();
293 }
294
295
296
297
298 protected ObjectFactory getObjectFactory() {
299 return getSession().getObjectFactory();
300 }
301
302
303
304
305
306 protected String getObjectId() {
307 String objectId = getId();
308 if (objectId == null) {
309 throw new IllegalStateException("Object Id is unknown!");
310 }
311
312 return objectId;
313 }
314
315
316
317
318 protected OperationContext getCreationContext() {
319 return creationContext;
320 }
321
322
323
324
325 protected String getPropertyQueryName(String propertyId) {
326 readLock();
327 try {
328 PropertyDefinition<?> propDef = objectType.getPropertyDefinitions().get(propertyId);
329 if (propDef == null) {
330 return null;
331 }
332
333 return propDef.getQueryName();
334 } finally {
335 readUnlock();
336 }
337 }
338
339
340
341 @Override
342 public void delete() {
343 delete(true);
344 }
345
346 @Override
347 public void delete(boolean allVersions) {
348 readLock();
349 try {
350 getSession().delete(this, allVersions);
351 } finally {
352 readUnlock();
353 }
354 }
355
356
357
358 @Override
359 public CmisObject updateProperties(Map<String, ?> properties) {
360 ObjectId objectId = updateProperties(properties, true);
361 if (objectId == null) {
362 return null;
363 }
364
365 if (!getObjectId().equals(objectId.getId())) {
366 return getSession().getObject(objectId, getCreationContext());
367 }
368
369 return this;
370 }
371
372 @Override
373 public ObjectId updateProperties(Map<String, ?> properties, boolean refresh) {
374 if (isNullOrEmpty(properties)) {
375 throw new IllegalArgumentException("Properties must not be empty!");
376 }
377
378 readLock();
379 String newObjectId = null;
380 try {
381 String objectId = getObjectId();
382 Holder<String> objectIdHolder = new Holder<String>(objectId);
383
384 String changeToken = getChangeToken();
385 Holder<String> changeTokenHolder = new Holder<String>(changeToken);
386
387 Set<Updatability> updatebility = EnumSet.noneOf(Updatability.class);
388 updatebility.add(Updatability.READWRITE);
389
390
391 Boolean isCheckedOut = getPropertyValue(PropertyIds.IS_VERSION_SERIES_CHECKED_OUT);
392 if (Boolean.TRUE.equals(isCheckedOut)) {
393 updatebility.add(Updatability.WHENCHECKEDOUT);
394 }
395
396
397 getBinding().getObjectService().updateProperties(getRepositoryId(), objectIdHolder, changeTokenHolder,
398 getObjectFactory().convertProperties(properties, this.objectType, this.secondaryTypes,
399 updatebility),
400 null);
401
402 newObjectId = objectIdHolder.getValue();
403
404
405 getSession().removeObjectFromCache(objectId);
406 } finally {
407 readUnlock();
408 }
409
410 if (refresh) {
411 refresh();
412 }
413
414 if (newObjectId == null) {
415 return null;
416 }
417
418 return getSession().createObjectId(newObjectId);
419 }
420
421 @Override
422 public CmisObject updateProperties(Map<String, ?> properties, List<String> addSecondaryTypeIds,
423 List<String> removeSecondaryTypeIds) {
424 ObjectId objectId = updateProperties(properties, addSecondaryTypeIds, removeSecondaryTypeIds, true);
425 if (objectId == null) {
426 return null;
427 }
428
429 if (!getObjectId().equals(objectId.getId())) {
430 return getSession().getObject(objectId, getCreationContext());
431 }
432
433 return this;
434 }
435
436 @Override
437 public ObjectId updateProperties(Map<String, ?> properties, List<String> addSecondaryTypeIds,
438 List<String> removeSecondaryTypeIds, boolean refresh) {
439 if ((addSecondaryTypeIds == null || addSecondaryTypeIds.isEmpty())
440 && (removeSecondaryTypeIds == null || removeSecondaryTypeIds.isEmpty())) {
441 return updateProperties(properties, refresh);
442 }
443
444 List<String> secondaryTypeIds = new ArrayList<String>();
445
446 readLock();
447 try {
448
449 if (!this.properties.containsKey(PropertyIds.SECONDARY_OBJECT_TYPE_IDS)) {
450 throw new IllegalStateException("Secondary Object Type Ids are not available!");
451 }
452
453
454 if (secondaryTypes != null) {
455 for (SecondaryType type : secondaryTypes) {
456 if (removeSecondaryTypeIds == null || !removeSecondaryTypeIds.contains(type.getId())) {
457 secondaryTypeIds.add(type.getId());
458 }
459 }
460 }
461
462 if (addSecondaryTypeIds != null) {
463 for (String addId : addSecondaryTypeIds) {
464 if (!secondaryTypeIds.contains(addId)) {
465 secondaryTypeIds.add(addId);
466 }
467 }
468 }
469 } finally {
470 readUnlock();
471 }
472
473
474 Map<String, Object> newProperties = new HashMap<String, Object>();
475 if (properties != null) {
476 newProperties.putAll(properties);
477 }
478 newProperties.put(PropertyIds.SECONDARY_OBJECT_TYPE_IDS, secondaryTypeIds);
479
480 return updateProperties(newProperties, refresh);
481 }
482
483 @Override
484 public CmisObject rename(String newName) {
485 if (newName == null || newName.length() == 0) {
486 throw new IllegalArgumentException("New name must not be empty!");
487 }
488
489 Map<String, Object> prop = new HashMap<String, Object>(2);
490 prop.put(PropertyIds.NAME, newName);
491
492 return updateProperties(prop);
493 }
494
495 @Override
496 public ObjectId rename(String newName, boolean refresh) {
497 if (newName == null || newName.length() == 0) {
498 throw new IllegalArgumentException("New name must not be empty!");
499 }
500
501 Map<String, Object> prop = new HashMap<String, Object>(2);
502 prop.put(PropertyIds.NAME, newName);
503
504 return updateProperties(prop, refresh);
505 }
506
507
508
509 @Override
510 public ObjectType getBaseType() {
511 BaseTypeId baseTypeId = getBaseTypeId();
512 if (baseTypeId == null) {
513 return null;
514 }
515
516 return getSession().getTypeDefinition(baseTypeId.value());
517 }
518
519 @Override
520 public BaseTypeId getBaseTypeId() {
521 String baseType = getPropertyValue(PropertyIds.BASE_TYPE_ID);
522 if (baseType == null) {
523 return null;
524 }
525
526 return BaseTypeId.fromValue(baseType);
527 }
528
529 @Override
530 public String getChangeToken() {
531 return getPropertyValue(PropertyIds.CHANGE_TOKEN);
532 }
533
534 @Override
535 public String getCreatedBy() {
536 return getPropertyValue(PropertyIds.CREATED_BY);
537 }
538
539 @Override
540 public GregorianCalendar getCreationDate() {
541 return getPropertyValue(PropertyIds.CREATION_DATE);
542 }
543
544 @Override
545 public String getId() {
546 return getPropertyValue(PropertyIds.OBJECT_ID);
547 }
548
549 @Override
550 public GregorianCalendar getLastModificationDate() {
551 return getPropertyValue(PropertyIds.LAST_MODIFICATION_DATE);
552 }
553
554 @Override
555 public String getLastModifiedBy() {
556 return getPropertyValue(PropertyIds.LAST_MODIFIED_BY);
557 }
558
559 @Override
560 public String getName() {
561 return getPropertyValue(PropertyIds.NAME);
562 }
563
564 @Override
565 public String getDescription() {
566 return getPropertyValue(PropertyIds.DESCRIPTION);
567 }
568
569 @Override
570 public List<Property<?>> getProperties() {
571 readLock();
572 try {
573 return Collections.unmodifiableList(new ArrayList<Property<?>>(properties.values()));
574 } finally {
575 readUnlock();
576 }
577 }
578
579 @Override
580 @SuppressWarnings("unchecked")
581 public <T> Property<T> getProperty(String id) {
582 readLock();
583 try {
584 return (Property<T>) properties.get(id);
585 } finally {
586 readUnlock();
587 }
588 }
589
590 @Override
591 @SuppressWarnings("unchecked")
592 public <T> T getPropertyValue(String id) {
593 Property<T> property = getProperty(id);
594 if (property == null) {
595 return null;
596 }
597
598 return (T) property.getValue();
599 }
600
601 @Override
602 public ObjectType getType() {
603 readLock();
604 try {
605 return objectType;
606 } finally {
607 readUnlock();
608 }
609 }
610
611 @Override
612 public List<SecondaryType> getSecondaryTypes() {
613 readLock();
614 try {
615 return secondaryTypes;
616 } finally {
617 readUnlock();
618 }
619 }
620
621 @Override
622 public List<ObjectType> findObjectType(String id) {
623 List<ObjectType> result = null;
624
625 readLock();
626 try {
627 if (objectType.getPropertyDefinitions().containsKey(id)) {
628 result = new ArrayList<ObjectType>();
629 result.add(objectType);
630 }
631
632 if (secondaryTypes != null) {
633 for (SecondaryType secondaryType : secondaryTypes) {
634 if (secondaryType.getPropertyDefinitions() != null
635 && secondaryType.getPropertyDefinitions().containsKey(id)) {
636 if (result == null) {
637 result = new ArrayList<ObjectType>();
638 }
639 result.add(secondaryType);
640 }
641 }
642 }
643 } finally {
644 readUnlock();
645 }
646
647 return result;
648 }
649
650
651
652 @Override
653 public AllowableActions getAllowableActions() {
654 readLock();
655 try {
656 return allowableActions;
657 } finally {
658 readUnlock();
659 }
660 }
661
662 @Override
663 public boolean hasAllowableAction(Action action) {
664 if (action == null) {
665 throw new IllegalArgumentException("Action must be set!");
666 }
667
668 AllowableActions currentAllowableActions = getAllowableActions();
669 if (currentAllowableActions == null || currentAllowableActions.getAllowableActions() == null) {
670 throw new IllegalStateException("Allowable Actions are not available!");
671 }
672
673 return currentAllowableActions.getAllowableActions().contains(action);
674 }
675
676
677
678 @Override
679 public List<Rendition> getRenditions() {
680 readLock();
681 try {
682 return renditions;
683 } finally {
684 readUnlock();
685 }
686 }
687
688
689
690 public Acl getAcl(boolean onlyBasicPermissions) {
691 String objectId = getObjectId();
692 return getBinding().getAclService().getAcl(getRepositoryId(), objectId, onlyBasicPermissions, null);
693 }
694
695 @Override
696 public Acl applyAcl(List<Ace> addAces, List<Ace> removeAces, AclPropagation aclPropagation) {
697 Acl result = getSession().applyAcl(this, addAces, removeAces, aclPropagation);
698
699 refresh();
700
701 return result;
702 }
703
704 @Override
705 public Acl addAcl(List<Ace> addAces, AclPropagation aclPropagation) {
706 return applyAcl(addAces, null, aclPropagation);
707 }
708
709 @Override
710 public Acl removeAcl(List<Ace> removeAces, AclPropagation aclPropagation) {
711 return applyAcl(null, removeAces, aclPropagation);
712 }
713
714 @Override
715 public Acl setAcl(List<Ace> aces) {
716 Acl result = getSession().setAcl(this, aces);
717
718 refresh();
719
720 return result;
721 }
722
723 @Override
724 public Acl getAcl() {
725 readLock();
726 try {
727 return acl;
728 } finally {
729 readUnlock();
730 }
731 }
732
733 @Override
734 public Set<String> getPermissionsForPrincipal(String principalId) {
735 if (principalId == null) {
736 throw new IllegalArgumentException("Principal must be set!");
737 }
738
739 Acl currentAcl = getAcl();
740
741 if (currentAcl == null) {
742 throw new IllegalStateException("ACLs are not available!");
743 }
744
745 if (isNullOrEmpty(acl.getAces())) {
746 return Collections.emptySet();
747 }
748
749 HashSet<String> result = new HashSet<String>();
750
751 for (Ace ace : acl.getAces()) {
752 if (principalId.equals(ace.getPrincipalId()) && ace.getPermissions() != null) {
753 result.addAll(ace.getPermissions());
754 }
755 }
756
757 return result;
758 }
759
760
761
762 @Override
763 public void applyPolicy(ObjectId... policyIds) {
764 readLock();
765 try {
766 getSession().applyPolicy(this, policyIds);
767 } finally {
768 readUnlock();
769 }
770
771 refresh();
772 }
773
774 @Override
775 public void applyPolicy(ObjectId policyId, boolean refresh) {
776 readLock();
777 try {
778 getSession().applyPolicy(this, policyId);
779 } finally {
780 readUnlock();
781 }
782
783 if (refresh) {
784 refresh();
785 }
786 }
787
788 @Override
789 public void removePolicy(ObjectId... policyIds) {
790 readLock();
791 try {
792 getSession().removePolicy(this, policyIds);
793 } finally {
794 readUnlock();
795 }
796
797 refresh();
798 }
799
800 @Override
801 public void removePolicy(ObjectId policyId, boolean refresh) {
802 readLock();
803 try {
804 getSession().removePolicy(this, policyId);
805 } finally {
806 readUnlock();
807 }
808
809 if (refresh) {
810 refresh();
811 }
812 }
813
814 @Override
815 public List<Policy> getPolicies() {
816 readLock();
817 try {
818 if (policies != null || policyIds == null) {
819 return policies;
820 }
821 } finally {
822 readUnlock();
823 }
824
825 writeLock();
826 try {
827 if (policies == null) {
828 policies = new ArrayList<Policy>(policyIds.size());
829 for (String pid : policyIds) {
830 try {
831 CmisObject policy = session.getObject(pid);
832 if (policy instanceof Policy) {
833 policies.add((Policy) policy);
834 }
835 } catch (CmisObjectNotFoundException nfe) {
836
837 }
838 }
839 }
840
841 return policies;
842 } finally {
843 writeUnlock();
844 }
845 }
846
847 @Override
848 public List<ObjectId> getPolicyIds() {
849 readLock();
850 try {
851 if (policyIds == null) {
852 return null;
853 }
854
855 List<ObjectId> result = new ArrayList<ObjectId>(policyIds.size());
856 for (String pid : policyIds) {
857 result.add(session.createObjectId(pid));
858 }
859
860 return result;
861 } finally {
862 readUnlock();
863 }
864 }
865
866
867
868 @Override
869 public List<Relationship> getRelationships() {
870 readLock();
871 try {
872 return this.relationships;
873 } finally {
874 readUnlock();
875 }
876 }
877
878
879
880 @Override
881 public List<CmisExtensionElement> getExtensions(ExtensionLevel level) {
882 List<CmisExtensionElement> ext = extensions.get(level);
883 if (ext == null) {
884 return null;
885 }
886
887 return Collections.unmodifiableList(ext);
888 }
889
890
891
892 @Override
893 public <T> T getAdapter(Class<T> adapterInterface) {
894 return null;
895 }
896
897
898
899 @Override
900 public long getRefreshTimestamp() {
901 readLock();
902 try {
903 return this.refreshTimestamp;
904 } finally {
905 readUnlock();
906 }
907 }
908
909 @Override
910 public void refresh() {
911 writeLock();
912 try {
913 String objectId = getObjectId();
914
915 OperationContext oc = getCreationContext();
916
917
918 ObjectData objectData = getSession().getBinding().getObjectService().getObject(getRepositoryId(), objectId,
919 oc.getFilterString(), oc.isIncludeAllowableActions(), oc.getIncludeRelationships(),
920 oc.getRenditionFilterString(), oc.isIncludePolicies(), oc.isIncludeAcls(), null);
921
922
923 initialize(session, session.getTypeDefinition(objectType.getId()), objectData, creationContext);
924 } finally {
925 writeUnlock();
926 }
927 }
928
929 @Override
930 public void refreshIfOld(long durationInMillis) {
931 writeLock();
932 try {
933 if (this.refreshTimestamp < System.currentTimeMillis() - durationInMillis) {
934 refresh();
935 }
936 } finally {
937 writeUnlock();
938 }
939 }
940
941 @Override
942 public String toString() {
943 readLock();
944 try {
945 if (objectType == null) {
946 return "<unknown>";
947 }
948
949 return objectType.getBaseTypeId() + " (" + objectType.getId() + "): " + getId();
950 } finally {
951 readUnlock();
952 }
953 }
954 }