docs: JcrVersionNode checkin( AbstractJcrNode node ) throws RepositoryException {
          checkVersionable(node);
  
          if (node.isNew() || node.isModified()) {
              throw new InvalidItemStateException(JcrI18n.noPendingChangesAllowed.text());
          }
  
          // Check this separately since it throws a different type of exception
          if (node.isLocked() && !node.holdsLock()) {
              throw new LockException(JcrI18n.lockTokenNotHeld.text(node.getPath()));
          }
  
          if (node.getProperty(JcrLexicon.MERGE_FAILED) != null) {
              throw new VersionException(JcrI18n.pendingMergeConflicts.text(node.getPath()));
          }
  
          javax.jcr.Property isCheckedOut = node.getProperty(JcrLexicon.IS_CHECKED_OUT);
          if (!isCheckedOut.getBoolean()) {
              return node.getBaseVersion();
          }
  
          // Collect some of the information about the node that we'll need ...
          SessionCache cache = cache();
          NodeKey versionedKey = node.key();
          Path versionHistoryPath = versionHistoryPathFor(versionedKey);
          CachedNode cachedNode = node.node();
          DateTime now = session().dateFactory().create();
  
          // Create the system content that we'll use to update the system branch ...
          SessionCache systemSession = session.createSystemCache(false);
          SystemContent systemContent = new SystemContent(systemSession);
  
          MutableCachedNode version = null;
          try {
              // Create a new version in the history for this node; this initializes the version history if it is missing ...
              List<Property> versionableProps = new ArrayList<Property>();
              addVersionedPropertiesFor(node, false, versionableProps);
  
              AtomicReference<MutableCachedNode> frozen = new AtomicReference<MutableCachedNode>();
              version = systemContent.recordNewVersion(cachedNode, cache, versionHistoryPath, null, versionableProps, now, frozen);
              NodeKey historyKey = version.getParentKey(systemSession);
  
              // Update the node's 'mix:versionable' properties, using a new session ...
              SessionCache versionSession = session.spawnSessionCache(false);
              MutableCachedNode versionableNode = versionSession.mutable(versionedKey);
              PropertyFactory props = propertyFactory();
              ReferenceFactory refFactory = session.referenceFactory();
              Reference historyRef = refFactory.create(historyKey, true);
              Reference baseVersionRef = refFactory.create(version.getKey(), true);
              versionableNode.setProperty(versionSession, props.create(JcrLexicon.VERSION_HISTORY, historyRef));
              versionableNode.setProperty(versionSession, props.create(JcrLexicon.BASE_VERSION, baseVersionRef));
              versionableNode.setProperty(versionSession, props.create(JcrLexicon.IS_CHECKED_OUT, Boolean.FALSE));
              // The 'jcr:predecessors' set to an empty array, per Section 15.2 in JSR-283
              versionableNode.setProperty(versionSession, props.create(JcrLexicon.PREDECESSORS, new Object[] {}));
  
              // Now process the children of the versionable node, and add them under the frozen node ...
              MutableCachedNode frozenNode = frozen.get();
              for (ChildReference childRef : versionableNode.getChildReferences(versionSession)) {
                  AbstractJcrNode child = session.node(childRef.getKey(), null, versionedKey);
                  versionNodeAt(child, childRef.getName(), frozenNode, false, versionSession, systemSession);
              }
  
              // Now save all of the changes.
              // the system session must be saved first so that its nodes are cleared first from the shared ws cache
              // this is required because there are references from the other session pointing towards the system session
              systemSession.save(versionSession, null);
          } finally {
              // TODO: Versioning: may want to catch this block and retry, if the new version name couldn't be created
          }
          // return the version node from the system cache, not this session's cache because this session is asynchronously 
          // notified of system ws changes and may not always get the latest data immediately
          return (JcrVersionNode)session.node(version, systemSession, Type.VERSION, null);
      }