001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 * 019 */ 020package org.apache.directory.server.core.trigger; 021 022 023import java.text.ParseException; 024import java.util.ArrayList; 025import java.util.Collections; 026import java.util.HashMap; 027import java.util.List; 028import java.util.Map; 029import java.util.Set; 030 031import javax.naming.directory.SearchControls; 032 033import org.apache.directory.api.ldap.model.constants.SchemaConstants; 034import org.apache.directory.api.ldap.model.entry.Attribute; 035import org.apache.directory.api.ldap.model.entry.Entry; 036import org.apache.directory.api.ldap.model.entry.Modification; 037import org.apache.directory.api.ldap.model.entry.Value; 038import org.apache.directory.api.ldap.model.exception.LdapException; 039import org.apache.directory.api.ldap.model.exception.LdapOperationException; 040import org.apache.directory.api.ldap.model.filter.EqualityNode; 041import org.apache.directory.api.ldap.model.filter.ExprNode; 042import org.apache.directory.api.ldap.model.message.AliasDerefMode; 043import org.apache.directory.api.ldap.model.name.Dn; 044import org.apache.directory.api.ldap.model.schema.AttributeType; 045import org.apache.directory.api.ldap.model.schema.NormalizerMappingResolver; 046import org.apache.directory.api.ldap.model.schema.SchemaManager; 047import org.apache.directory.api.ldap.model.schema.normalizers.OidNormalizer; 048import org.apache.directory.api.ldap.trigger.TriggerSpecification; 049import org.apache.directory.api.ldap.trigger.TriggerSpecificationParser; 050import org.apache.directory.server.constants.ApacheSchemaConstants; 051import org.apache.directory.server.core.api.CoreSession; 052import org.apache.directory.server.core.api.DirectoryService; 053import org.apache.directory.server.core.api.filtering.EntryFilteringCursor; 054import org.apache.directory.server.core.api.interceptor.context.ModifyOperationContext; 055import org.apache.directory.server.core.api.interceptor.context.SearchOperationContext; 056import org.apache.directory.server.core.api.partition.Partition; 057import org.apache.directory.server.core.api.partition.PartitionNexus; 058import org.apache.directory.server.i18n.I18n; 059import org.slf4j.Logger; 060import org.slf4j.LoggerFactory; 061 062 063/** 064 * A cache for Trigger Specifications which responds to specific events to 065 * perform cache house keeping as trigger subentries are added, deleted 066 * and modified. 067 * 068 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 069 */ 070public class TriggerSpecCache 071{ 072 /** the attribute id for prescriptive trigger: prescriptiveTrigger */ 073 private static final String PRESCRIPTIVE_TRIGGER_ATTR = "prescriptiveTriggerSpecification"; 074 075 /** the logger for this class */ 076 private static final Logger LOG = LoggerFactory.getLogger( TriggerSpecCache.class ); 077 078 /** a map of strings to TriggerSpecification collections */ 079 private final Map<Dn, List<TriggerSpecification>> triggerSpecs = new HashMap<>(); 080 081 /** a handle on the partition nexus */ 082 private final PartitionNexus nexus; 083 084 /** a normalizing TriggerSpecification parser */ 085 private final TriggerSpecificationParser triggerSpecParser; 086 087 088 /** 089 * Creates a TriggerSpecification cache. 090 * 091 * @param directoryService the directory service core 092 * @throws LdapException with problems initializing cache 093 */ 094 public TriggerSpecCache( DirectoryService directoryService ) throws LdapException 095 { 096 this.nexus = directoryService.getPartitionNexus(); 097 final SchemaManager schemaManager = directoryService.getSchemaManager(); 098 099 triggerSpecParser = new TriggerSpecificationParser( new NormalizerMappingResolver() 100 { 101 @Override 102 public Map<String, OidNormalizer> getNormalizerMapping() throws Exception 103 { 104 return schemaManager.getNormalizerMapping(); 105 } 106 } ); 107 108 initialize( directoryService ); 109 } 110 111 112 private void initialize( DirectoryService directoryService ) throws LdapException 113 { 114 // search all naming contexts for trigger subentenries 115 // generate TriggerSpecification arrays for each subentry 116 // add that subentry to the hash 117 Set<String> suffixes = nexus.listSuffixes(); 118 119 AttributeType objectClassAt = directoryService.getSchemaManager(). 120 getAttributeType( SchemaConstants.OBJECT_CLASS_AT ); 121 122 for ( String suffix : suffixes ) 123 { 124 Dn baseDn = directoryService.getDnFactory().create( suffix ); 125 ExprNode filter = new EqualityNode<String>( objectClassAt, 126 new Value( objectClassAt, ApacheSchemaConstants.TRIGGER_EXECUTION_SUBENTRY_OC ) ); 127 SearchControls ctls = new SearchControls(); 128 ctls.setSearchScope( SearchControls.SUBTREE_SCOPE ); 129 130 CoreSession adminSession = directoryService.getAdminSession(); 131 132 Partition partition = nexus.getPartition( baseDn ); 133 SearchOperationContext searchOperationContext = new SearchOperationContext( adminSession, baseDn, 134 filter, ctls ); 135 searchOperationContext.setAliasDerefMode( AliasDerefMode.DEREF_ALWAYS ); 136 searchOperationContext.setPartition( partition ); 137 searchOperationContext.setTransaction( partition.beginReadTransaction() ); 138 139 EntryFilteringCursor results = nexus.search( searchOperationContext ); 140 141 try 142 { 143 while ( results.next() ) 144 { 145 Entry resultEntry = results.get(); 146 Dn subentryDn = resultEntry.getDn(); 147 Attribute triggerSpec = resultEntry.get( PRESCRIPTIVE_TRIGGER_ATTR ); 148 149 if ( triggerSpec == null ) 150 { 151 LOG.warn( "Found triggerExecutionSubentry '{}' without any {}", subentryDn, PRESCRIPTIVE_TRIGGER_ATTR ); 152 continue; 153 } 154 155 Dn normSubentryDn = subentryDn; 156 157 if ( !subentryDn.isSchemaAware() ) 158 { 159 normSubentryDn = new Dn( directoryService.getSchemaManager(), subentryDn ); 160 } 161 162 subentryAdded( normSubentryDn, resultEntry ); 163 } 164 165 results.close(); 166 } 167 catch ( Exception e ) 168 { 169 throw new LdapOperationException( e.getMessage(), e ); 170 } 171 } 172 } 173 174 175 private boolean hasPrescriptiveTrigger( Entry entry ) 176 { 177 // only do something if the entry contains prescriptiveTrigger 178 Attribute triggerSpec = entry.get( PRESCRIPTIVE_TRIGGER_ATTR ); 179 180 return triggerSpec != null; 181 } 182 183 184 public void subentryAdded( Dn normName, Entry entry ) 185 { 186 // only do something if the entry contains prescriptiveTrigger 187 Attribute triggerSpec = entry.get( PRESCRIPTIVE_TRIGGER_ATTR ); 188 189 if ( triggerSpec == null ) 190 { 191 return; 192 } 193 194 List<TriggerSpecification> subentryTriggerSpecs = new ArrayList<>(); 195 196 for ( Value value : triggerSpec ) 197 { 198 TriggerSpecification item = null; 199 200 try 201 { 202 item = triggerSpecParser.parse( value.getString() ); 203 subentryTriggerSpecs.add( item ); 204 } 205 catch ( ParseException e ) 206 { 207 String msg = I18n.err( I18n.ERR_73, item ); 208 LOG.error( msg, e ); 209 } 210 211 } 212 213 triggerSpecs.put( normName, subentryTriggerSpecs ); 214 } 215 216 217 public void subentryDeleted( Dn normName, Entry entry ) 218 { 219 if ( !hasPrescriptiveTrigger( entry ) ) 220 { 221 return; 222 } 223 224 triggerSpecs.remove( normName ); 225 } 226 227 228 public void subentryModified( ModifyOperationContext opContext, Entry entry ) 229 { 230 if ( !hasPrescriptiveTrigger( entry ) ) 231 { 232 return; 233 } 234 235 Dn normName = opContext.getDn(); 236 List<Modification> mods = opContext.getModItems(); 237 238 boolean isTriggerSpecModified = false; 239 240 for ( Modification mod : mods ) 241 { 242 isTriggerSpecModified |= mod.getAttribute().contains( PRESCRIPTIVE_TRIGGER_ATTR ); 243 } 244 245 if ( isTriggerSpecModified ) 246 { 247 subentryDeleted( normName, entry ); 248 subentryAdded( normName, entry ); 249 } 250 } 251 252 253 public List<TriggerSpecification> getSubentryTriggerSpecs( Dn subentryDn ) 254 { 255 List<TriggerSpecification> subentryTriggerSpecs = triggerSpecs.get( subentryDn ); 256 257 if ( subentryTriggerSpecs == null ) 258 { 259 return Collections.emptyList(); 260 } 261 262 return Collections.unmodifiableList( subentryTriggerSpecs ); 263 } 264 265 266 public void subentryRenamed( Dn oldName, Dn newName ) 267 { 268 triggerSpecs.put( newName, triggerSpecs.remove( oldName ) ); 269 } 270}