@Override
  public void notify(ChangeSet changeSet) {
    if (!systemWorkspaceName.equals(changeSet.getWorkspaceName())) {
      // The change does not affect the 'system' workspace, so skip it ...
      return;
    }
    if (context.getProcessId().equals(changeSet.getProcessKey())) {
      // We generated these changes, so skip them ...
      return;
    }

    // Now process the changes ...
    Set<Name> nodeTypesToRefresh = new HashSet<Name>();
    Set<Name> nodeTypesToDelete = new HashSet<Name>();
    for (Change change : changeSet) {
      if (change instanceof NodeAdded) {
        NodeAdded added = (NodeAdded) change;
        Path addedPath = added.getPath();
        if (nodeTypesPath.isAncestorOf(addedPath)) {
          // Get the name of the node type ...
          Name nodeTypeName = addedPath.getSegment(2).getName();
          nodeTypesToRefresh.add(nodeTypeName);
        }
      } else if (change instanceof NodeRemoved) {
        NodeRemoved removed = (NodeRemoved) change;
        Path removedPath = removed.getPath();
        if (nodeTypesPath.isAncestorOf(removedPath)) {
          // Get the name of the node type ...
          Name nodeTypeName = removedPath.getSegment(2).getName();
          if (removedPath.size() == 3) {
            nodeTypesToDelete.add(nodeTypeName);
          } else {
            // It's a child defn or property defn ...
            if (!nodeTypesToDelete.contains(nodeTypeName)) {
              // The child defn or property defn is being removed but the node type is not ...
              nodeTypesToRefresh.add(nodeTypeName);
            }
          }
        }
      } else if (change instanceof PropertyChanged) {
        PropertyChanged propChanged = (PropertyChanged) change;
        Path changedPath = propChanged.getPathToNode();
        if (nodeTypesPath.isAncestorOf(changedPath)) {
          // Get the name of the node type ...
          Name nodeTypeName = changedPath.getSegment(2).getName();
          nodeTypesToRefresh.add(nodeTypeName);
        }
      } // we don't care about node moves (don't happen) or property added/removed (handled by node
        // add/remove)
    }

    if (nodeTypesToRefresh.isEmpty() && nodeTypesToDelete.isEmpty()) {
      // No changes
      return;
    }

    // There were at least some changes ...
    this.nodeTypesLock.writeLock().lock();
    try {
      // Re-register the node types that were changed or added ...
      SessionCache systemCache = repository.createSystemSession(context, false);
      SystemContent system = new SystemContent(systemCache);
      Collection<NodeTypeDefinition> nodeTypes = system.readNodeTypes(nodeTypesToRefresh);
      registerNodeTypes(nodeTypes, false, false, false);

      // Unregister those that were removed ...
      unregisterNodeType(nodeTypesToDelete, false);
    } catch (Throwable e) {
      logger.error(e, JcrI18n.errorRefreshingNodeTypes, repository.name());
    } finally {
      this.nodeTypesLock.writeLock().unlock();
    }
  }