View Javadoc

1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *  
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *  
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License. 
18   *  
19   */
20  package org.apache.directory.server.core.trigger;
21  
22  
23  import org.apache.directory.server.constants.ApacheSchemaConstants;
24  import org.apache.directory.server.constants.ServerDNConstants;
25  import org.apache.directory.server.core.CoreSession;
26  import org.apache.directory.server.core.DefaultCoreSession;
27  import org.apache.directory.server.core.DirectoryService;
28  import org.apache.directory.server.core.authn.LdapPrincipal;
29  import org.apache.directory.server.core.entry.ClonedServerEntry;
30  import org.apache.directory.server.core.entry.ServerEntry;
31  import org.apache.directory.server.core.filtering.EntryFilteringCursor;
32  import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
33  import org.apache.directory.server.core.interceptor.context.SearchOperationContext;
34  import org.apache.directory.server.core.partition.PartitionNexus;
35  import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
36  import org.apache.directory.shared.ldap.constants.AuthenticationLevel;
37  import org.apache.directory.shared.ldap.constants.SchemaConstants;
38  import org.apache.directory.shared.ldap.entry.EntryAttribute;
39  import org.apache.directory.shared.ldap.entry.Modification;
40  import org.apache.directory.shared.ldap.entry.Value;
41  import org.apache.directory.shared.ldap.filter.EqualityNode;
42  import org.apache.directory.shared.ldap.filter.ExprNode;
43  import org.apache.directory.shared.ldap.message.AliasDerefMode;
44  import org.apache.directory.shared.ldap.name.LdapDN;
45  import org.apache.directory.shared.ldap.schema.NormalizerMappingResolver;
46  import org.apache.directory.shared.ldap.schema.OidNormalizer;
47  import org.apache.directory.shared.ldap.trigger.TriggerSpecification;
48  import org.apache.directory.shared.ldap.trigger.TriggerSpecificationParser;
49  import org.apache.directory.shared.ldap.entry.client.ClientStringValue;
50  import org.slf4j.Logger;
51  import org.slf4j.LoggerFactory;
52  
53  import javax.naming.directory.SearchControls;
54  import java.text.ParseException;
55  import java.util.ArrayList;
56  import java.util.Collections;
57  import java.util.HashMap;
58  import java.util.Iterator;
59  import java.util.List;
60  import java.util.Map;
61  
62  
63  /**
64   * A cache for Trigger Specifications which responds to specific events to
65   * perform cache house keeping as trigger subentries are added, deleted
66   * and modified.
67   *
68   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
69   * @version $Rev:$
70   */
71  public class TriggerSpecCache
72  {
73      /** the attribute id for prescriptive trigger: prescriptiveTrigger */
74      private static final String PRESCRIPTIVE_TRIGGER_ATTR = "prescriptiveTriggerSpecification";
75  
76      /** the logger for this class */
77      private static final Logger LOG = LoggerFactory.getLogger( TriggerSpecCache.class );
78  
79      /** a map of strings to TriggerSpecification collections */
80      private final Map<String, List<TriggerSpecification>> triggerSpecs = new HashMap<String, List<TriggerSpecification>>();
81      /** a handle on the partition nexus */
82      private final PartitionNexus nexus;
83      /** a normalizing TriggerSpecification parser */
84      private final TriggerSpecificationParser triggerSpecParser;
85  
86  
87      /**
88       * Creates a TriggerSpecification cache.
89       *
90       * @param directoryService the directory service core
91       * @throws NamingException with problems initializing cache
92       */
93      public TriggerSpecCache( DirectoryService directoryService ) throws Exception
94      {
95          this.nexus = directoryService.getPartitionNexus();
96          final AttributeTypeRegistry registry = directoryService.getRegistries().getAttributeTypeRegistry();
97          triggerSpecParser = new TriggerSpecificationParser( new NormalizerMappingResolver()
98              {
99                  public Map<String, OidNormalizer> getNormalizerMapping() throws Exception
100                 {
101                     return registry.getNormalizerMapping();
102                 }
103             });
104         initialize( directoryService );
105     }
106 
107 
108     private void initialize( DirectoryService directoryService ) throws Exception
109     {
110         // search all naming contexts for trigger subentenries
111         // generate TriggerSpecification arrays for each subentry
112         // add that subentry to the hash
113         Iterator<String> suffixes = nexus.listSuffixes( null );
114         
115         while ( suffixes.hasNext() )
116         {
117             String suffix = suffixes.next();
118             LdapDN baseDn = new LdapDN( suffix );
119             ExprNode filter = new EqualityNode<String>( SchemaConstants.OBJECT_CLASS_AT, 
120                     new ClientStringValue( ApacheSchemaConstants.TRIGGER_EXECUTION_SUBENTRY_OC ) );
121             SearchControls ctls = new SearchControls();
122             ctls.setSearchScope( SearchControls.SUBTREE_SCOPE );
123             
124             LdapDN adminDn = new LdapDN( ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED );
125             adminDn.normalize( directoryService.getRegistries().getAttributeTypeRegistry().getNormalizerMapping() );
126             CoreSession adminSession = new DefaultCoreSession( 
127                 new LdapPrincipal( adminDn, AuthenticationLevel.STRONG ), directoryService );
128             EntryFilteringCursor results = nexus.search( new SearchOperationContext( 
129                 adminSession, baseDn, AliasDerefMode.DEREF_ALWAYS, filter, ctls ) );
130             
131             while ( results.next() )
132             {
133                 ClonedServerEntry resultEntry = results.get();
134                 LdapDN subentryDn = resultEntry.getDn();
135                 EntryAttribute triggerSpec = resultEntry.get( PRESCRIPTIVE_TRIGGER_ATTR );
136                 
137                 if ( triggerSpec == null )
138                 {
139                     LOG.warn( "Found triggerExecutionSubentry '" + subentryDn + "' without any " + PRESCRIPTIVE_TRIGGER_ATTR );
140                     continue;
141                 }
142 
143                 LdapDN normSubentryName = subentryDn.normalize( directoryService.getRegistries()
144                     .getAttributeTypeRegistry().getNormalizerMapping() );
145                 subentryAdded( normSubentryName, resultEntry );
146             }
147             
148             results.close();
149         }
150     }
151 
152 
153     private boolean hasPrescriptiveTrigger( ServerEntry entry ) throws Exception
154     {
155         // only do something if the entry contains prescriptiveTrigger
156         EntryAttribute triggerSpec = entry.get( PRESCRIPTIVE_TRIGGER_ATTR );
157 
158         return triggerSpec != null;
159     }
160 
161 
162     public void subentryAdded( LdapDN normName, ServerEntry entry ) throws Exception
163     {
164         // only do something if the entry contains prescriptiveTrigger
165         EntryAttribute triggerSpec = entry.get( PRESCRIPTIVE_TRIGGER_ATTR );
166         
167         if ( triggerSpec == null )
168         {
169             return;
170         }
171         
172         List<TriggerSpecification> subentryTriggerSpecs = new ArrayList<TriggerSpecification>();
173         
174         for ( Value<?> value:triggerSpec )
175         {
176             TriggerSpecification item = null;
177 
178             try
179             {
180                 item = triggerSpecParser.parse( ( String ) value.get() );
181                 subentryTriggerSpecs.add( item );
182             }
183             catch ( ParseException e )
184             {
185                 String msg = "TriggerSpecification parser failure on '" + item + "'. Cannnot add Trigger Specificaitons to TriggerSpecCache.";
186                 LOG.error( msg, e );
187             }
188             
189         }
190         
191         triggerSpecs.put( normName.toString(), subentryTriggerSpecs );
192     }
193 
194 
195     public void subentryDeleted( LdapDN normName, ServerEntry entry ) throws Exception
196     {
197         if ( !hasPrescriptiveTrigger( entry ) )
198         {
199             return;
200         }
201 
202         triggerSpecs.remove( normName.toString() );
203     }
204 
205 
206     public void subentryModified( ModifyOperationContext opContext, ServerEntry entry ) throws Exception
207     {
208         if ( !hasPrescriptiveTrigger( entry ) )
209         {
210             return;
211         }
212 
213         LdapDN normName = opContext.getDn();
214         List<Modification> mods = opContext.getModItems();
215 
216         boolean isTriggerSpecModified = false;
217 
218         for ( Modification mod : mods )
219         {
220             isTriggerSpecModified |= mod.getAttribute().contains( PRESCRIPTIVE_TRIGGER_ATTR );
221         }
222         
223         if ( isTriggerSpecModified )
224         {
225             subentryDeleted( normName, entry );
226             subentryAdded( normName, entry );
227         }
228     }
229 
230 
231     public List<TriggerSpecification> getSubentryTriggerSpecs( String subentryDn )
232     {
233         List<TriggerSpecification> subentryTriggerSpecs = triggerSpecs.get( subentryDn );
234         if ( subentryTriggerSpecs == null )
235         {
236             return Collections.emptyList();
237         }
238         return Collections.unmodifiableList( subentryTriggerSpecs );
239     }
240 
241 
242     public void subentryRenamed( LdapDN oldName, LdapDN newName )
243     {
244         triggerSpecs.put( newName.toString(), triggerSpecs.remove( oldName.toString() ) );
245     }
246 }