/**
  * Refresh the node types from the stored representation.
  *
  * @return true if there was at least one node type found, or false if there were none
  */
 protected boolean refreshFromSystem() {
   this.nodeTypesLock.writeLock().lock();
   try {
     // Re-read and re-register all of the node types ...
     SessionCache systemCache = repository.createSystemSession(context, true);
     SystemContent system = new SystemContent(systemCache);
     Collection<NodeTypeDefinition> nodeTypes = system.readAllNodeTypes();
     if (nodeTypes.isEmpty()) return false;
     registerNodeTypes(nodeTypes, false, false, false);
     return true;
   } catch (Throwable e) {
     logger.error(e, JcrI18n.errorRefreshingNodeTypes, repository.name());
     return false;
   } finally {
     this.nodeTypesLock.writeLock().unlock();
   }
 }
 protected RepositoryIndexes readIndexDefinitions() {
   // There were at least some changes ...
   NodeTypes nodeTypes = repository.nodeTypeManager().getNodeTypes();
   try {
     // Read the affected index definitions ...
     SessionCache systemCache = repository.createSystemSession(context, false);
     SystemContent system = new SystemContent(systemCache);
     Collection<IndexDefinition> indexDefns = system.readAllIndexDefinitions(providers.keySet());
     this.indexes = new Indexes(context, indexDefns, nodeTypes);
     return this.indexes;
   } catch (WorkspaceNotFoundException e) {
     // This happens occasionally when shutting down ...
   } catch (Throwable e) {
     logger.error(e, JcrI18n.errorRefreshingIndexDefinitions, repository.name());
   }
   return indexes;
 }
 /**
  * Refresh the node types from the stored representation.
  *
  * @return true if there was at least one node type found, or false if there were none
  */
 protected boolean refreshFromSystem() {
   Lock lock = this.namespacesLock.writeLock();
   try {
     lock.lock();
     // Re-read and re-register all of the namespaces ...
     SessionCache systemCache = repository.createSystemSession(context, false);
     SystemContent system = new SystemContent(systemCache);
     Collection<Namespace> namespaces = system.readAllNamespaces();
     if (namespaces.isEmpty()) return false;
     this.cache.clear();
     this.cache.register(namespaces);
   } catch (Throwable e) {
     logger.error(e, JcrI18n.errorRefreshingNodeTypes, repository.name());
   } finally {
     lock.unlock();
   }
   return true;
 }
  @Override
  public void unregisterIndexes(String... indexNames)
      throws NoSuchIndexException, RepositoryException {
    if (indexNames == null || indexNames.length == 0) return;

    // Remove the definition from the system area ...
    SessionCache systemCache = repository.createSystemSession(context, false);
    SystemContent system = new SystemContent(systemCache);
    for (String indexName : indexNames) {
      IndexDefinition defn = indexes.getIndexDefinitions().get(indexName);
      if (defn == null) {
        throw new NoSuchIndexException(
            JcrI18n.indexDoesNotExist.text(indexName, repository.name()));
      }
      system.remove(defn);
    }
    system.save();

    // Refresh the immutable snapshot ...
    this.indexes = readIndexDefinitions();
  }
 private final SystemContent systemContent(boolean readOnly) {
   SessionCache systemCache = repository.createSystemSession(context, readOnly);
   return new SystemContent(systemCache);
 }
  @Override
  public void registerIndexes(IndexDefinition[] indexDefinitions, boolean allowUpdate)
      throws InvalidIndexDefinitionException, IndexExistsException {
    CheckArg.isNotNull(indexDefinitions, "indexDefinitions");

    // Before we do anything, validate each of the index definitions and throw an exception ...
    RepositoryNodeTypeManager nodeTypeManager = repository.nodeTypeManager();
    List<IndexDefinition> validated = new ArrayList<>(indexDefinitions.length);
    Problems problems = new SimpleProblems();
    for (IndexDefinition defn : indexDefinitions) {
      String name = defn.getName();
      String providerName = defn.getProviderName();

      if (name == null) {
        problems.addError(JcrI18n.indexMustHaveName, defn, repository.name());
        continue;
      }
      if (indexes.getIndexDefinitions().containsKey(name) && !allowUpdate) {
        // Throw this one immediately ...
        String msg = JcrI18n.indexAlreadyExists.text(defn.getName(), repository.name());
        throw new IndexExistsException(msg);
      }
      if (providerName == null) {
        problems.addError(JcrI18n.indexMustHaveProviderName, defn.getName(), repository.name());
        continue;
      }
      if (defn.hasSingleColumn()) {
        IndexColumnDefinition columnDefn = defn.getColumnDefinition(0);
        Name propName =
            context.getValueFactories().getNameFactory().create(columnDefn.getPropertyName());
        switch (defn.getKind()) {
          case UNIQUE_VALUE:
            if (NON_UNIQUE_PROPERTY_NAMES.contains(propName)) {
              problems.addError(
                  JcrI18n.unableToCreateUniqueIndexForColumn,
                  defn.getName(),
                  columnDefn.getPropertyName());
            }
            break;
          case ENUMERATED_VALUE:
            if (NON_ENUMERATED_PROPERTY_NAMES.contains(propName)) {
              problems.addError(
                  JcrI18n.unableToCreateEnumeratedIndexForColumn,
                  defn.getName(),
                  columnDefn.getPropertyName());
            }
            break;
          case VALUE:
          case NODE_TYPE:
          case TEXT:
            break;
        }
      } else {
        // Mulitple columns ...
        if (defn.getKind() == IndexKind.NODE_TYPE) {
          // must be single-column indexes
          problems.addError(JcrI18n.nodeTypeIndexMustHaveOneColumn, defn.getName());
        }
      }
      IndexProvider provider = providers.get(providerName);
      if (provider == null) {
        problems.addError(JcrI18n.indexProviderDoesNotExist, defn.getName(), repository.name());
      } else {
        // Perform some default validations that should be applied to all providers...
        provider.validateDefaultColumnTypes(context, defn, problems);

        // Then have the provider perform any custom validations
        provider.validateProposedIndex(context, defn, nodeTypeManager, problems);

        // Create an instance of our own definition implementation ...
        defn = RepositoryIndexDefinition.createFrom(defn, true);

        validated.add(defn);
      }
    }
    if (problems.hasErrors()) {
      String msg = JcrI18n.invalidIndexDefinitions.text(repository.name(), problems);
      throw new InvalidIndexDefinitionException(new JcrProblems(problems), msg);
    }

    SessionCache systemCache = repository.createSystemSession(context, false);
    SystemContent system = new SystemContent(systemCache);
    for (IndexDefinition defn : validated) {
      String providerName = defn.getProviderName();

      // Determine if the index should be enabled ...
      defn = RepositoryIndexDefinition.createFrom(defn, providers.containsKey(providerName));

      // Write the definition to the system area ...
      system.store(defn, allowUpdate);
    }
    // Save the changes ...
    systemCache.save();

    // Refresh the immutable snapshot ...
    this.indexes = readIndexDefinitions();
  }
  @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();
    }
  }
  List<JcrNodeType> registerNodeTypes(
      Iterable<NodeTypeDefinition> nodeTypeDefns,
      boolean failIfNodeTypeDefinitionsExist,
      boolean skipIfNodeTypeDefinitionExists,
      boolean persist)
      throws InvalidNodeTypeDefinitionException, NodeTypeExistsException, RepositoryException {

    if (nodeTypeDefns == null) {
      return Collections.emptyList();
    }

    List<JcrNodeType> typesPendingRegistration = new ArrayList<JcrNodeType>();

    try {
      nodeTypesLock.writeLock().lock();
      final NodeTypes nodeTypes = this.nodeTypesCache;

      for (NodeTypeDefinition nodeTypeDefn : nodeTypeDefns) {
        if (nodeTypeDefn instanceof JcrNodeTypeTemplate) {
          // Switch to use this context, so names are properly prefixed ...
          nodeTypeDefn = ((JcrNodeTypeTemplate) nodeTypeDefn).with(context);
        }
        Name internalName = nodeTypes.nameFactory().create(nodeTypeDefn.getName());
        if (internalName == null || internalName.getLocalName().length() == 0) {
          throw new InvalidNodeTypeDefinitionException(JcrI18n.invalidNodeTypeName.text());
        }

        boolean found = nodeTypes.hasNodeType(internalName);
        if (found && failIfNodeTypeDefinitionsExist) {
          String name = nodeTypeDefn.getName();
          throw new NodeTypeExistsException(internalName, JcrI18n.nodeTypeAlreadyExists.text(name));
        }
        if (found && skipIfNodeTypeDefinitionExists) continue;

        List<JcrNodeType> supertypes =
            nodeTypes.supertypesFor(nodeTypeDefn, typesPendingRegistration);
        JcrNodeType nodeType = nodeTypeFrom(nodeTypeDefn, supertypes);

        typesPendingRegistration.add(nodeType);
      }

      if (!typesPendingRegistration.isEmpty()) {
        // Make sure the nodes have primary types that are either already registered, or pending
        // registration ...
        validateTypes(typesPendingRegistration);

        // Validate each of types that should be registered
        for (JcrNodeType typePendingRegistration : typesPendingRegistration) {
          nodeTypes.validate(
              typePendingRegistration,
              Arrays.asList(typePendingRegistration.getDeclaredSupertypes()),
              typesPendingRegistration);
        }

        SystemContent system = null;
        if (persist) {
          SessionCache systemCache = repository.createSystemSession(context, false);
          system = new SystemContent(systemCache);
        }

        for (JcrNodeType nodeType : typesPendingRegistration) {
          if (system != null) system.store(nodeType, true);
        }

        // Create the new cache ...
        NodeTypes newNodeTypes = nodeTypes.with(typesPendingRegistration);

        // Save the changes ...
        if (system != null) system.save();

        // And finally update the capabilities cache ...
        this.nodeTypesCache = newNodeTypes;
        this.schemata = null;
      }
    } finally {
      nodeTypesLock.writeLock().unlock();
    }

    return typesPendingRegistration;
  }
  /**
   * Allows the collection of node types to be unregistered if they are not referenced by other node
   * types as supertypes, default primary types of child nodes, or required primary types of child
   * nodes.
   *
   * @param nodeTypeNames the names of the node types to be unregistered
   * @param failIfNodeTypesAreUsed true if this method should fail to unregister the named node
   *     types if any of the node types are still in use by nodes, or false if this method should
   *     not perform such a check
   * @throws NoSuchNodeTypeException if any of the node type names do not correspond to a registered
   *     node type
   * @throws InvalidNodeTypeDefinitionException if any of the node types with the given names cannot
   *     be unregistered because they are the supertype, one of the required primary types, or a
   *     default primary type of a node type that is not being unregistered.
   * @throws RepositoryException if any other error occurs
   */
  void unregisterNodeType(Collection<Name> nodeTypeNames, boolean failIfNodeTypesAreUsed)
      throws NoSuchNodeTypeException, InvalidNodeTypeDefinitionException, RepositoryException {
    CheckArg.isNotNull(nodeTypeNames, "nodeTypeNames");
    if (nodeTypeNames.isEmpty()) return;

    if (failIfNodeTypesAreUsed) {
      long start = System.nanoTime();
      // Search the content graph to make sure that this type isn't being used
      for (Name nodeTypeName : nodeTypeNames) {
        if (isNodeTypeInUse(nodeTypeName)) {
          String name = nodeTypeName.getString(context.getNamespaceRegistry());
          throw new InvalidNodeTypeDefinitionException(
              JcrI18n.cannotUnregisterInUseType.text(name));
        }
      }
      long time =
          TimeUnit.MILLISECONDS.convert(Math.abs(System.nanoTime() - start), TimeUnit.NANOSECONDS);
      logger.debug(
          "{0} milliseconds to check if any of these node types are unused before unregistering them: {1}",
          time, nodeTypeNames);
    }

    try {
      /*
       * Grab an exclusive lock on this data to keep other nodes from being added/saved while the unregistration checks are occurring
       */
      List<JcrNodeType> removedNodeTypes = new ArrayList<JcrNodeType>(nodeTypeNames.size());
      nodeTypesLock.writeLock().lock();
      final NodeTypes nodeTypes = this.nodeTypesCache;

      for (Name nodeTypeName : nodeTypeNames) {
        /*
         * Check that the type names are valid
         */
        if (nodeTypeName == null) {
          throw new NoSuchNodeTypeException(JcrI18n.invalidNodeTypeName.text());
        }
        String name = nodeTypeName.getString(context.getNamespaceRegistry());

        JcrNodeType foundNodeType = nodeTypes.getNodeType(nodeTypeName);
        if (foundNodeType == null) {
          throw new NoSuchNodeTypeException(JcrI18n.noSuchNodeType.text(name));
        }
        removedNodeTypes.add(foundNodeType);

        /*
         * Check that no other node definitions have dependencies on any of the named types
         */
        for (JcrNodeType nodeType : nodeTypes.getAllNodeTypes()) {
          // If this node is also being unregistered, don't run checks against it
          if (nodeTypeNames.contains(nodeType.getInternalName())) {
            continue;
          }

          for (JcrNodeType supertype : nodeType.supertypes()) {
            if (nodeTypeName.equals(supertype.getInternalName())) {
              throw new InvalidNodeTypeDefinitionException(
                  JcrI18n.cannotUnregisterSupertype.text(name, supertype.getName()));
            }
          }

          for (JcrNodeDefinition childNode : nodeType.childNodeDefinitions()) {
            NodeType defaultPrimaryType = childNode.getDefaultPrimaryType();
            if (defaultPrimaryType != null && name.equals(defaultPrimaryType.getName())) {
              throw new InvalidNodeTypeDefinitionException(
                  JcrI18n.cannotUnregisterDefaultPrimaryType.text(
                      name, nodeType.getName(), childNode.getName()));
            }
            if (childNode.requiredPrimaryTypeNameSet().contains(nodeTypeName)) {
              throw new InvalidNodeTypeDefinitionException(
                  JcrI18n.cannotUnregisterRequiredPrimaryType.text(
                      name, nodeType.getName(), childNode.getName()));
            }
          }
        }
      }

      // Create the new cache ...
      NodeTypes newNodeTypes = nodeTypes.without(removedNodeTypes);

      // Remove the node types from persistent storage ...
      SessionCache system = repository.createSystemSession(context, false);
      SystemContent systemContent = new SystemContent(system);
      systemContent.unregisterNodeTypes(
          removedNodeTypes.toArray(new JcrNodeType[removedNodeTypes.size()]));
      systemContent.save();

      // Now change the cache ...
      this.nodeTypesCache = newNodeTypes;
      this.schemata = null;
    } finally {
      nodeTypesLock.writeLock().unlock();
    }
  }