@Override public synchronized void register(IndexProvider provider) throws RepositoryException { if (providers.containsKey(provider.getName())) { throw new IndexProviderExistsException( JcrI18n.indexProviderAlreadyExists.text(provider.getName(), repository.name())); } // Set the repository name field ... Reflection.setValue(provider, "repositoryName", repository.name()); // Set the logger instance Reflection.setValue(provider, "logger", ExtensionLogger.getLogger(provider.getClass())); if (initialized.get()) { // This manager is already initialized, so we have to initialize the new provider ... doInitialize(provider); } // Do this last so that it doesn't show up in the list of providers before it's properly // initialized ... IndexProvider existing = providers.putIfAbsent(provider.getName(), provider); if (existing != null) { throw new IndexProviderExistsException( JcrI18n.indexProviderAlreadyExists.text(provider.getName(), repository.name())); } // Re-read the index definitions in case there were disabled index definitions that used the // now-available provider ... readIndexDefinitions(); // Refresh the index writer ... refreshIndexWriter(); }
RepositoryIndexManager(JcrRepository.RunningState repository, RepositoryConfiguration config) { this.repository = repository; this.config = config; this.context = repository.context(); this.systemWorkspaceName = this.repository.repositoryCache().getSystemWorkspaceName(); PathFactory pathFactory = this.context.getValueFactories().getPathFactory(); this.indexesPath = pathFactory.createAbsolutePath(JcrLexicon.SYSTEM, ModeShapeLexicon.INDEXES); // Set up the index providers ... this.components = config.getIndexProviders(); for (Component component : components) { try { IndexProvider provider = component.createInstance(ScanningQueryEngine.class.getClassLoader()); register(provider); } catch (Throwable t) { if (t.getCause() != null) { t = t.getCause(); } this.repository.error( t, JcrI18n.unableToInitializeIndexProvider, component, repository.name(), t.getMessage()); } } }
/** * Check if the named node type is in use in any workspace in the repository * * @param nodeTypeName the name of the node type to check * @return true if at least one node is using that type; false otherwise * @throws InvalidQueryException if there is an error searching for uses of the named node type */ boolean isNodeTypeInUse(Name nodeTypeName) throws InvalidQueryException { String nodeTypeString = nodeTypeName.getString(context.getNamespaceRegistry()); String expression = "SELECT * from [" + nodeTypeString + "] LIMIT 1"; TypeSystem typeSystem = context.getValueFactories().getTypeSystem(); // Parsing must be done now ... QueryCommand command = queryParser.parseQuery(expression, typeSystem); assert command != null : "Could not parse " + expression; Schemata schemata = getRepositorySchemata(); // Now query the entire repository for any nodes that use this node type ... RepositoryCache repoCache = repository.repositoryCache(); RepositoryQueryManager queryManager = repository.queryManager(); Set<String> workspaceNames = repoCache.getWorkspaceNames(); Map<String, NodeCache> overridden = null; NodeTypes nodeTypes = repository.nodeTypeManager().getNodeTypes(); RepositoryIndexes indexDefns = repository.queryManager().getIndexes(); CancellableQuery query = queryManager.query( context, repoCache, workspaceNames, overridden, command, schemata, indexDefns, nodeTypes, null, null); try { QueryResults result = query.execute(); if (result.isEmpty()) return false; if (result.getRowCount() < 0) { // Try to get the first row ... NodeSequence seq = result.getRows(); Batch batch = seq.nextBatch(); while (batch != null) { if (batch.hasNext()) return true; // It's not common for the first batch may be empty, but it's possible. So try the next // batch ... batch = seq.nextBatch(); } return false; } return result.getRowCount() > 0; } catch (RepositoryException e) { logger.error(e, JcrI18n.errorCheckingNodeTypeUsage, nodeTypeName, e.getLocalizedMessage()); return true; } }
RepositoryNodeTypeManager with( JcrRepository.RunningState repository, boolean includeColumnsForInheritedProperties, boolean includePseudoColumnsInSelectStar) { assert this.systemWorkspaceName.equals(repository.repositoryCache().getSystemWorkspaceName()); PathFactory pathFactory = repository.context().getValueFactories().getPathFactory(); Path nodeTypesPath = pathFactory.createAbsolutePath(JcrLexicon.SYSTEM, JcrLexicon.NODE_TYPES); assert this.nodeTypesPath.equals(nodeTypesPath); RepositoryNodeTypeManager result = new RepositoryNodeTypeManager( repository, includeColumnsForInheritedProperties, includePseudoColumnsInSelectStar); // Now copy the node types from this cache into the new manager's cache ... // (If we didn't do this, we'd have to refresh from the system storage) result.nodeTypesCache = result.nodeTypesCache.with(this.nodeTypesCache.getAllNodeTypes()); return result; }
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() { 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(); } }
/** * 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(); }
/** * Initialize the supplied provider. * * @param provider the provider; may not be null * @throws RepositoryException if there is a problem initializing the provider */ protected void doInitialize(IndexProvider provider) throws RepositoryException { // Set the execution context instance ... Reflection.setValue(provider, "context", repository.context()); // Set the environment Reflection.setValue(provider, "environment", repository.environment()); provider.initialize(); // If successful, call the 'postInitialize' method reflectively (due to inability to call // directly) ... Method postInitialize = Reflection.findMethod(IndexProvider.class, "postInitialize"); Reflection.invokeAccessibly(provider, postInitialize, new Object[] {}); if (logger.isDebugEnabled()) { logger.debug( "Successfully initialized index provider '{0}' in repository '{1}'", provider.getName(), repository.name()); } }
void shutdown() { for (IndexProvider provider : providers.values()) { try { provider.shutdown(); } catch (RepositoryException e) { logger.error( e, JcrI18n.errorShuttingDownIndexProvider, repository.name(), provider.getName(), e.getMessage()); } } }
RepositoryNodeTypeManager( JcrRepository.RunningState repository, boolean includeColumnsForInheritedProperties, boolean includePseudoColumnsInSelectStar) { this.repository = repository; this.context = repository.context(); this.nameFactory = this.context.getValueFactories().getNameFactory(); this.systemWorkspaceName = this.repository.repositoryCache().getSystemWorkspaceName(); PathFactory pathFactory = this.context.getValueFactories().getPathFactory(); this.nodeTypesPath = pathFactory.createAbsolutePath(JcrLexicon.SYSTEM, JcrLexicon.NODE_TYPES); this.nodeTypesCache = new NodeTypes(this.context); this.includeColumnsForInheritedProperties = includeColumnsForInheritedProperties; this.includePseudoColumnsInSelectStar = includePseudoColumnsInSelectStar; queryParser = new BasicSqlQueryParser(); }
@Override public void unregister(String providerName) throws RepositoryException { IndexProvider provider = providers.remove(providerName); if (provider == null) { throw new NoSuchProviderException( JcrI18n.indexProviderDoesNotExist.text(providerName, repository.name())); } if (initialized.get()) { provider.shutdown(); } // Re-read the index definitions in case there were disabled index definitions that used the // now-available provider ... readIndexDefinitions(); // Refresh the index writer ... refreshIndexWriter(); }
private JcrPropertyDefinition propertyDefinitionFrom(PropertyDefinition propDefn) throws RepositoryException { Name propertyName = nameFactory.create(propDefn.getName()); int onParentVersionBehavior = propDefn.getOnParentVersion(); int requiredType = propDefn.getRequiredType(); boolean mandatory = propDefn.isMandatory(); boolean multiple = propDefn.isMultiple(); boolean autoCreated = propDefn.isAutoCreated(); boolean isProtected = propDefn.isProtected(); boolean fullTextSearchable = propDefn.isFullTextSearchable(); boolean queryOrderable = propDefn.isQueryOrderable(); Value[] defaultValues = propDefn.getDefaultValues(); JcrValue[] jcrDefaultValues = null; if (defaultValues != null) { jcrDefaultValues = new JcrValue[defaultValues.length]; for (int i = 0; i != defaultValues.length; ++i) { Value value = defaultValues[i]; jcrDefaultValues[i] = new JcrValue(this.context.getValueFactories(), value); } } String[] valueConstraints = propDefn.getValueConstraints(); String[] queryOperators = propDefn.getAvailableQueryOperators(); if (valueConstraints == null) valueConstraints = new String[0]; NodeKey prototypeKey = repository.repositoryCache().getSystemKey(); return new JcrPropertyDefinition( this.context, null, prototypeKey, propertyName, onParentVersionBehavior, autoCreated, mandatory, isProtected, jcrDefaultValues, requiredType, valueConstraints, multiple, fullTextSearchable, queryOrderable, queryOperators); }
private JcrNodeType nodeTypeFrom(NodeTypeDefinition nodeType, List<JcrNodeType> supertypes) throws RepositoryException { PropertyDefinition[] propDefns = nodeType.getDeclaredPropertyDefinitions(); NodeDefinition[] childDefns = nodeType.getDeclaredChildNodeDefinitions(); List<JcrPropertyDefinition> properties = new ArrayList<JcrPropertyDefinition>(); List<JcrNodeDefinition> childNodes = new ArrayList<JcrNodeDefinition>(); if (propDefns != null) { for (PropertyDefinition propDefn : propDefns) { properties.add(propertyDefinitionFrom(propDefn)); } } if (childDefns != null) { for (NodeDefinition childNodeDefn : childDefns) { childNodes.add(childNodeDefinitionFrom(childNodeDefn)); } } Name name = nameFactory.create(nodeType.getName()); Name primaryItemName = nameFactory.create(nodeType.getPrimaryItemName()); boolean mixin = nodeType.isMixin(); boolean isAbstract = nodeType.isAbstract(); boolean queryable = nodeType.isQueryable(); boolean orderableChildNodes = nodeType.hasOrderableChildNodes(); NodeKey prototypeKey = repository.repositoryCache().getSystemKey(); return new JcrNodeType( prototypeKey, this.context, null, this, name, supertypes, primaryItemName, childNodes, properties, mixin, isAbstract, queryable, orderableChildNodes); }
synchronized void importIndexDefinitions() throws RepositoryException { RepositoryConfiguration.Indexes indexes = config.getIndexes(); if (indexes.isEmpty()) return; List<IndexDefinition> defns = new ArrayList<>(); for (String indexName : indexes.getIndexNames()) { IndexDefinition defn = indexes.getIndex(indexName); if (defn != null) defns.add(defn); } if (!defns.isEmpty()) { IndexDefinition[] array = defns.toArray(new IndexDefinition[defns.size()]); registerIndexes(array, true); // Wait while the indexes get created ... try { Thread.sleep(500L + array.length * 50L); } catch (Exception e) { throw new SystemFailureException(e); } // We have to index the '/jcr:system' content, since it was created before these indexes were // registered ... repository.queryManager().reindexSystemContent(); } }
private JcrNodeDefinition childNodeDefinitionFrom(NodeDefinition childNodeDefn) { Name childNodeName = nameFactory.create(childNodeDefn.getName()); Name defaultPrimaryTypeName = nameFactory.create(childNodeDefn.getDefaultPrimaryTypeName()); int onParentVersion = childNodeDefn.getOnParentVersion(); boolean mandatory = childNodeDefn.isMandatory(); boolean allowsSns = childNodeDefn.allowsSameNameSiblings(); boolean autoCreated = childNodeDefn.isAutoCreated(); boolean isProtected = childNodeDefn.isProtected(); Name[] requiredTypes; String[] requiredTypeNames = childNodeDefn.getRequiredPrimaryTypeNames(); if (requiredTypeNames != null) { List<Name> names = new ArrayList<Name>(requiredTypeNames.length); for (String typeName : requiredTypeNames) { names.add(nameFactory.create(typeName)); } requiredTypes = names.toArray(new Name[names.size()]); } else { requiredTypes = new Name[0]; } NodeKey prototypeKey = repository.repositoryCache().getSystemKey(); return new JcrNodeDefinition( this.context, null, prototypeKey, childNodeName, onParentVersion, autoCreated, mandatory, isProtected, allowsSns, defaultPrimaryTypeName, requiredTypes); }
protected ScanningTasks notify(ChangeSet changeSet) { if (changeSet.getWorkspaceName() == null) { // This is a change to the workspaces or repository metadata ... // Refresh the index definitions ... RepositoryIndexes indexes = readIndexDefinitions(); ScanningTasks feedback = new ScanningTasks(); if (!indexes.getIndexDefinitions().isEmpty()) { // Build up the names of the added and removed workspace names ... Set<String> addedWorkspaces = new HashSet<>(); Set<String> removedWorkspaces = new HashSet<>(); for (Change change : changeSet) { if (change instanceof WorkspaceAdded) { WorkspaceAdded added = (WorkspaceAdded) change; addedWorkspaces.add(added.getWorkspaceName()); } else if (change instanceof WorkspaceRemoved) { WorkspaceRemoved removed = (WorkspaceRemoved) change; removedWorkspaces.add(removed.getWorkspaceName()); } } if (!addedWorkspaces.isEmpty() || !removedWorkspaces.isEmpty()) { // Figure out which providers need to be called, and which definitions go with those // providers ... Map<String, List<IndexDefinition>> defnsByProvider = new HashMap<>(); for (IndexDefinition defn : indexes.getIndexDefinitions().values()) { String providerName = defn.getProviderName(); List<IndexDefinition> defns = defnsByProvider.get(providerName); if (defns == null) { defns = new ArrayList<>(); defnsByProvider.put(providerName, defns); } defns.add(defn); } // Then for each provider ... for (Map.Entry<String, List<IndexDefinition>> entry : defnsByProvider.entrySet()) { String providerName = entry.getKey(); WorkspaceIndexChanges changes = new WorkspaceIndexChanges(entry.getValue(), addedWorkspaces, removedWorkspaces); IndexProvider provider = providers.get(providerName); if (provider == null) continue; provider.notify( changes, repository.changeBus(), repository.nodeTypeManager(), repository.repositoryCache().getWorkspaceNames(), feedback.forProvider(providerName)); } } } return feedback; } if (!systemWorkspaceName.equals(changeSet.getWorkspaceName())) { // The change does not affect the 'system' workspace, so skip it ... return null; } // It is simple to listen to all local and remote changes. Therefore, any changes made locally // to the index definitions // will be propagated through the cached representation via this listener. AtomicReference<Map<Name, IndexChangeInfo>> changesByProviderName = new AtomicReference<>(); for (Change change : changeSet) { if (change instanceof NodeAdded) { NodeAdded added = (NodeAdded) change; Path addedPath = added.getPath(); if (indexesPath.isAncestorOf(addedPath)) { // Get the name of the affected provider ... Name providerName = addedPath.getSegment(2).getName(); if (addedPath.size() > 3) { // Adding an index (or column definition), but all we care about is the name of the // index Name indexName = addedPath.getSegment(3).getName(); changeInfoForProvider(changesByProviderName, providerName).changed(indexName); } } } else if (change instanceof NodeRemoved) { NodeRemoved removed = (NodeRemoved) change; Path removedPath = removed.getPath(); if (indexesPath.isAncestorOf(removedPath)) { // Get the name of the affected provider ... Name providerName = removedPath.getSegment(2).getName(); if (removedPath.size() > 4) { // It's a column definition being removed, so the index is changed ... Name indexName = removedPath.getSegment(3).getName(); changeInfoForProvider(changesByProviderName, providerName).removed(indexName); } else if (removedPath.size() > 3) { // Removing an index (or column definition), but all we care about is the name of the // index Name indexName = removedPath.getSegment(3).getName(); changeInfoForProvider(changesByProviderName, providerName).removed(indexName); } else if (removedPath.size() == 3) { // The whole provider was removed ... changeInfoForProvider(changesByProviderName, providerName).removedAll(); } } } else if (change instanceof PropertyChanged) { PropertyChanged propChanged = (PropertyChanged) change; Path changedPath = propChanged.getPathToNode(); if (indexesPath.isAncestorOf(changedPath)) { if (changedPath.size() > 3) { // Adding an index (or column definition), but all we care about is the name of the // index Name providerName = changedPath.getSegment(2).getName(); Name indexName = changedPath.getSegment(3).getName(); changeInfoForProvider(changesByProviderName, providerName).changed(indexName); } } } // we don't care about node moves (don't happen) or property added/removed (handled by node // add/remove) } if (changesByProviderName.get() == null || changesByProviderName.get().isEmpty()) { // No changes to the indexes ... return null; } // Refresh the index definitions ... RepositoryIndexes indexes = readIndexDefinitions(); // And notify the affected providers ... StringFactory strings = context.getValueFactories().getStringFactory(); ScanningTasks feedback = new ScanningTasks(); for (Map.Entry<Name, IndexChangeInfo> entry : changesByProviderName.get().entrySet()) { String providerName = strings.create(entry.getKey()); IndexProvider provider = providers.get(providerName); if (provider == null) continue; IndexChanges changes = new IndexChanges(); IndexChangeInfo info = entry.getValue(); if (info.removedAll) { // Get all of the definitions for this provider ... for (IndexDefinition defn : indexes.getIndexDefinitions().values()) { if (defn.getProviderName().equals(providerName)) changes.remove(defn.getName()); } } // Others might have been added or changed after the existing ones were removed ... for (Name name : info.removedIndexes) { changes.remove(strings.create(name)); } for (Name name : info.changedIndexes) { IndexDefinition defn = indexes.getIndexDefinitions().get(strings.create(name)); if (defn != null) changes.change(defn); } // Notify the provider ... try { provider.notify( changes, repository.changeBus(), repository.nodeTypeManager(), repository.repositoryCache().getWorkspaceNames(), feedback.forProvider(providerName)); } catch (RuntimeException e) { logger.error( e, JcrI18n.errorNotifyingProviderOfIndexChanges, providerName, repository.name(), e.getMessage()); } } // Finally swap the snapshot of indexes ... this.indexes = indexes; return feedback; }
@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(); }
/** * 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(); } }
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; }
@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(); } }
private final SystemContent systemContent(boolean readOnly) { SessionCache systemCache = repository.createSystemSession(context, readOnly); return new SystemContent(systemCache); }
/** * Initialize this manager by calling {@link IndexProvider#initialize()} on each of the * currently-registered providers. * * @return the information about the portions of the repository that need to be scanned to * (re)build indexes; null if no scanning is required */ protected synchronized ScanningTasks initialize() { if (initialized.get()) { // nothing to do ... return null; } // Initialize each of the providers, removing any that are not properly initialized ... for (Iterator<Map.Entry<String, IndexProvider>> providerIter = providers.entrySet().iterator(); providerIter.hasNext(); ) { IndexProvider provider = providerIter.next().getValue(); try { doInitialize(provider); } catch (Throwable t) { if (t.getCause() != null) { t = t.getCause(); } repository.error( t, JcrI18n.unableToInitializeIndexProvider, provider.getName(), repository.name(), t.getMessage()); providerIter.remove(); } } // Re-read the index definitions in case there were disabled index definitions that used the // now-available provider ... RepositoryIndexes indexes = readIndexDefinitions(); // Notify the providers of all the index definitions (which we'll treat as "new" since we're // just starting up) ... ScanningTasks feedback = new ScanningTasks(); for (Iterator<Map.Entry<String, IndexProvider>> providerIter = providers.entrySet().iterator(); providerIter.hasNext(); ) { IndexProvider provider = providerIter.next().getValue(); if (provider == null) continue; final String providerName = provider.getName(); IndexChanges changes = new IndexChanges(); for (IndexDefinition indexDefn : indexes.getIndexDefinitions().values()) { if (!providerName.equals(indexDefn.getProviderName())) continue; changes.change(indexDefn); } // Even if there are no definitions, we still want to notify each of the providers ... try { provider.notify( changes, repository.changeBus(), repository.nodeTypeManager(), repository.repositoryCache().getWorkspaceNames(), feedback.forProvider(providerName)); } catch (RuntimeException e) { logger.error( e, JcrI18n.errorNotifyingProviderOfIndexChanges, providerName, repository.name(), e.getMessage()); } } // Refresh the index writer ... refreshIndexWriter(); initialized.set(true); return feedback; }