This project has retired. For details please refer to its Attic page.
AbstractCmisObject xref
View Javadoc

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