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 */ 020 package org.apache.directory.server.core.trigger; 021 022 023 import java.text.ParseException; 024 import java.util.ArrayList; 025 import java.util.Collections; 026 import java.util.HashMap; 027 import java.util.List; 028 import java.util.Map; 029 import java.util.Set; 030 031 import javax.naming.NamingException; 032 import javax.naming.directory.SearchControls; 033 034 import org.apache.directory.server.constants.ApacheSchemaConstants; 035 import org.apache.directory.server.constants.ServerDNConstants; 036 import org.apache.directory.server.core.CoreSession; 037 import org.apache.directory.server.core.DefaultCoreSession; 038 import org.apache.directory.server.core.DirectoryService; 039 import org.apache.directory.server.core.LdapPrincipal; 040 import org.apache.directory.server.core.entry.ClonedServerEntry; 041 import org.apache.directory.server.core.filtering.EntryFilteringCursor; 042 import org.apache.directory.server.core.interceptor.context.ModifyOperationContext; 043 import org.apache.directory.server.core.interceptor.context.SearchOperationContext; 044 import org.apache.directory.server.core.partition.PartitionNexus; 045 import org.apache.directory.server.i18n.I18n; 046 import org.apache.directory.shared.ldap.constants.AuthenticationLevel; 047 import org.apache.directory.shared.ldap.constants.SchemaConstants; 048 import org.apache.directory.shared.ldap.entry.StringValue; 049 import org.apache.directory.shared.ldap.entry.EntryAttribute; 050 import org.apache.directory.shared.ldap.entry.Modification; 051 import org.apache.directory.shared.ldap.entry.ServerEntry; 052 import org.apache.directory.shared.ldap.entry.Value; 053 import org.apache.directory.shared.ldap.filter.EqualityNode; 054 import org.apache.directory.shared.ldap.filter.ExprNode; 055 import org.apache.directory.shared.ldap.message.AliasDerefMode; 056 import org.apache.directory.shared.ldap.name.DN; 057 import org.apache.directory.shared.ldap.schema.NormalizerMappingResolver; 058 import org.apache.directory.shared.ldap.schema.SchemaManager; 059 import org.apache.directory.shared.ldap.schema.normalizers.OidNormalizer; 060 import org.apache.directory.shared.ldap.trigger.TriggerSpecification; 061 import org.apache.directory.shared.ldap.trigger.TriggerSpecificationParser; 062 import org.slf4j.Logger; 063 import org.slf4j.LoggerFactory; 064 065 066 /** 067 * A cache for Trigger Specifications which responds to specific events to 068 * perform cache house keeping as trigger subentries are added, deleted 069 * and modified. 070 * 071 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 072 * @version $Rev:$ 073 */ 074 public class TriggerSpecCache 075 { 076 /** the attribute id for prescriptive trigger: prescriptiveTrigger */ 077 private static final String PRESCRIPTIVE_TRIGGER_ATTR = "prescriptiveTriggerSpecification"; 078 079 /** the logger for this class */ 080 private static final Logger LOG = LoggerFactory.getLogger( TriggerSpecCache.class ); 081 082 /** a map of strings to TriggerSpecification collections */ 083 private final Map<String, List<TriggerSpecification>> triggerSpecs = new HashMap<String, List<TriggerSpecification>>(); 084 /** a handle on the partition nexus */ 085 private final PartitionNexus nexus; 086 /** a normalizing TriggerSpecification parser */ 087 private final TriggerSpecificationParser triggerSpecParser; 088 089 090 /** 091 * Creates a TriggerSpecification cache. 092 * 093 * @param directoryService the directory service core 094 * @throws NamingException with problems initializing cache 095 */ 096 public TriggerSpecCache( DirectoryService directoryService ) throws Exception 097 { 098 this.nexus = directoryService.getPartitionNexus(); 099 final SchemaManager schemaManager = directoryService.getSchemaManager(); 100 101 triggerSpecParser = new TriggerSpecificationParser( new NormalizerMappingResolver() 102 { 103 public Map<String, OidNormalizer> getNormalizerMapping() throws Exception 104 { 105 return schemaManager.getNormalizerMapping(); 106 } 107 }); 108 initialize( directoryService ); 109 } 110 111 112 private void initialize( DirectoryService directoryService ) throws Exception 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( null ); 118 119 for ( String suffix:suffixes ) 120 { 121 DN baseDn = new DN( suffix ); 122 ExprNode filter = new EqualityNode<String>( SchemaConstants.OBJECT_CLASS_AT, 123 new StringValue( ApacheSchemaConstants.TRIGGER_EXECUTION_SUBENTRY_OC ) ); 124 SearchControls ctls = new SearchControls(); 125 ctls.setSearchScope( SearchControls.SUBTREE_SCOPE ); 126 127 DN adminDn = new DN( ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED ); 128 adminDn.normalize( directoryService.getSchemaManager().getNormalizerMapping() ); 129 CoreSession adminSession = new DefaultCoreSession( 130 new LdapPrincipal( adminDn, AuthenticationLevel.STRONG ), directoryService ); 131 132 SearchOperationContext searchOperationContext = new SearchOperationContext( adminSession, baseDn, 133 filter, ctls ); 134 searchOperationContext.setAliasDerefMode( AliasDerefMode.DEREF_ALWAYS ); 135 136 EntryFilteringCursor results = nexus.search( searchOperationContext ); 137 138 while ( results.next() ) 139 { 140 ClonedServerEntry resultEntry = results.get(); 141 DN subentryDn = resultEntry.getDn(); 142 EntryAttribute triggerSpec = resultEntry.get( PRESCRIPTIVE_TRIGGER_ATTR ); 143 144 if ( triggerSpec == null ) 145 { 146 LOG.warn( "Found triggerExecutionSubentry '" + subentryDn + "' without any " + PRESCRIPTIVE_TRIGGER_ATTR ); 147 continue; 148 } 149 150 DN normSubentryName = subentryDn.normalize( directoryService.getSchemaManager() 151 .getNormalizerMapping() ); 152 subentryAdded( normSubentryName, resultEntry ); 153 } 154 155 results.close(); 156 } 157 } 158 159 160 private boolean hasPrescriptiveTrigger( ServerEntry entry ) throws Exception 161 { 162 // only do something if the entry contains prescriptiveTrigger 163 EntryAttribute triggerSpec = entry.get( PRESCRIPTIVE_TRIGGER_ATTR ); 164 165 return triggerSpec != null; 166 } 167 168 169 public void subentryAdded( DN normName, ServerEntry entry ) throws Exception 170 { 171 // only do something if the entry contains prescriptiveTrigger 172 EntryAttribute triggerSpec = entry.get( PRESCRIPTIVE_TRIGGER_ATTR ); 173 174 if ( triggerSpec == null ) 175 { 176 return; 177 } 178 179 List<TriggerSpecification> subentryTriggerSpecs = new ArrayList<TriggerSpecification>(); 180 181 for ( Value<?> value:triggerSpec ) 182 { 183 TriggerSpecification item = null; 184 185 try 186 { 187 item = triggerSpecParser.parse( value.getString() ); 188 subentryTriggerSpecs.add( item ); 189 } 190 catch ( ParseException e ) 191 { 192 String msg = I18n.err( I18n.ERR_73, item ); 193 LOG.error( msg, e ); 194 } 195 196 } 197 198 triggerSpecs.put( normName.getNormName(), subentryTriggerSpecs ); 199 } 200 201 202 public void subentryDeleted( DN normName, ServerEntry entry ) throws Exception 203 { 204 if ( !hasPrescriptiveTrigger( entry ) ) 205 { 206 return; 207 } 208 209 triggerSpecs.remove( normName.toString() ); 210 } 211 212 213 public void subentryModified( ModifyOperationContext opContext, ServerEntry entry ) throws Exception 214 { 215 if ( !hasPrescriptiveTrigger( entry ) ) 216 { 217 return; 218 } 219 220 DN normName = opContext.getDn(); 221 List<Modification> mods = opContext.getModItems(); 222 223 boolean isTriggerSpecModified = false; 224 225 for ( Modification mod : mods ) 226 { 227 isTriggerSpecModified |= mod.getAttribute().contains( PRESCRIPTIVE_TRIGGER_ATTR ); 228 } 229 230 if ( isTriggerSpecModified ) 231 { 232 subentryDeleted( normName, entry ); 233 subentryAdded( normName, entry ); 234 } 235 } 236 237 238 public List<TriggerSpecification> getSubentryTriggerSpecs( String subentryDn ) 239 { 240 List<TriggerSpecification> subentryTriggerSpecs = triggerSpecs.get( subentryDn ); 241 if ( subentryTriggerSpecs == null ) 242 { 243 return Collections.emptyList(); 244 } 245 return Collections.unmodifiableList( subentryTriggerSpecs ); 246 } 247 248 249 public void subentryRenamed( DN oldName, DN newName ) 250 { 251 triggerSpecs.put( newName.getNormName(), triggerSpecs.remove( oldName.getNormName() ) ); 252 } 253 }