View Javadoc

1   /*
2    *   Copyright 2004 The Apache Software Foundation
3    *
4    *   Licensed under the Apache License, Version 2.0 (the "License");
5    *   you may not use this file except in compliance with the License.
6    *   You may obtain a copy of the License at
7    *
8    *       http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *   Unless required by applicable law or agreed to in writing, software
11   *   distributed under the License is distributed on an "AS IS" BASIS,
12   *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *   See the License for the specific language governing permissions and
14   *   limitations under the License.
15   *
16   */
17  package org.apache.ldap.server.exception;
18  
19  
20  import org.apache.ldap.common.exception.LdapContextNotEmptyException;
21  import org.apache.ldap.common.exception.LdapNameAlreadyBoundException;
22  import org.apache.ldap.common.exception.LdapNameNotFoundException;
23  import org.apache.ldap.common.exception.LdapNamingException;
24  import org.apache.ldap.common.message.ResultCodeEnum;
25  import org.apache.ldap.common.name.LdapName;
26  import org.apache.ldap.server.BackingStore;
27  import org.apache.ldap.server.RootNexus;
28  import org.apache.ldap.server.interceptor.BaseInterceptor;
29  import org.apache.ldap.server.interceptor.InterceptorContext;
30  import org.apache.ldap.server.interceptor.NextInterceptor;
31  import org.apache.ldap.server.invocation.*;
32  
33  import javax.naming.Name;
34  import javax.naming.NamingEnumeration;
35  import javax.naming.NamingException;
36  import javax.naming.directory.Attribute;
37  import javax.naming.directory.Attributes;
38  
39  
40  /***
41   * An {@link org.apache.ldap.server.interceptor.Interceptor} that detects any operations that breaks integrity
42   * of {@link BackingStore} and terminates the current invocation chain by
43   * throwing a {@link NamingException}. Those operations include when an entry
44   * already exists at a DN and is added once again to the same DN.
45   *
46   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
47   * @version $Rev: 159272 $
48   */
49  public class ExceptionService extends BaseInterceptor
50  {
51      /***
52       * the root nexus of the system
53       */
54      private RootNexus nexus;
55  
56  
57      /***
58       * Creates an interceptor that is also the exception handling service.
59       */
60      public ExceptionService()
61      {
62      }
63  
64  
65      public void init( InterceptorContext ctx )
66      {
67          this.nexus = ctx.getRootNexus();
68      }
69  
70  
71      public void destroy()
72      {
73      }
74  
75  
76      /***
77       * In the pre-invocation state this interceptor method checks to see if the entry to be added already exists.  If it
78       * does an exception is raised.
79       */
80      protected void process( NextInterceptor nextInterceptor, Add call ) throws NamingException
81      {
82          // check if the entry already exists
83          Name normName = call.getNormalizedName();
84          String upName = call.getUserProvidedName();
85          if ( nexus.hasEntry( normName ) )
86          {
87              NamingException ne = new LdapNameAlreadyBoundException();
88              ne.setResolvedName( new LdapName( upName ) );
89              throw ne;
90          }
91  
92          Name parentDn = new LdapName( upName );
93          parentDn = parentDn.getSuffix( 1 );
94  
95          // check if we don't have the parent to add to
96          assertHasEntry( "Attempt to add under non-existant parent: ", parentDn );
97  
98          // check if we're trying to add to a parent that is an alias
99          Attributes attrs = nexus.lookup( normName.getSuffix( 1 ) );
100         Attribute objectClass = attrs.get( "objectClass" );
101         if ( objectClass.contains( "alias" ) )
102         {
103             String msg = "Attempt to add entry to alias '" + upName
104                     + "' not allowed.";
105             ResultCodeEnum rc = ResultCodeEnum.ALIASPROBLEM;
106             NamingException e = new LdapNamingException( msg, rc );
107             e.setResolvedName( parentDn );
108             throw e;
109         }
110 
111         nextInterceptor.process( call );
112     }
113 
114 
115     /***
116      * Checks to make sure the entry being deleted exists, and has no children, otherwise throws the appropriate
117      * LdapException.
118      */
119     protected void process( NextInterceptor nextInterceptor, Delete call ) throws NamingException
120     {
121         Name name = call.getName();
122         
123         // check if entry to delete exists
124         String msg = "Attempt to delete non-existant entry: ";
125         assertHasEntry( msg, name );
126 
127         // check if entry to delete has children (only leaves can be deleted)
128         boolean hasChildren = false;
129         NamingEnumeration list = nexus.list( name );
130         if ( list.hasMore() )
131         {
132             hasChildren = true;
133         }
134 
135         list.close();
136         if ( hasChildren )
137         {
138             LdapContextNotEmptyException e = new LdapContextNotEmptyException();
139             e.setResolvedName( name );
140             throw e;
141         }
142 
143         nextInterceptor.process( call );
144     }
145 
146 
147     /***
148      * Checks to see the base being searched exists, otherwise throws the appropriate LdapException.
149      */
150     protected void process( NextInterceptor nextInterceptor, List call ) throws NamingException
151     {
152         // check if entry to search exists
153         String msg = "Attempt to search under non-existant entry: ";
154         assertHasEntry( msg, call.getBaseName() );
155 
156         nextInterceptor.process( call );
157     }
158 
159 
160     /***
161      * Checks to make sure the entry being looked up exists other wise throws the appropriate LdapException.
162      */
163     protected void process( NextInterceptor nextInterceptor, Lookup call ) throws NamingException
164     {
165         String msg = "Attempt to lookup non-existant entry: ";
166         assertHasEntry( msg, call.getName() );
167 
168         nextInterceptor.process( call );
169     }
170 
171 
172     /***
173      * Checks to see the base being searched exists, otherwise throws the appropriate LdapException.
174      */
175     protected void process( NextInterceptor nextInterceptor, LookupWithAttrIds call ) throws NamingException
176     {
177         // check if entry to lookup exists
178         String msg = "Attempt to lookup non-existant entry: ";
179         assertHasEntry( msg, call.getName() );
180 
181         nextInterceptor.process( call );
182     }
183 
184 
185     /***
186      * Checks to see the entry being modified exists, otherwise throws the appropriate LdapException.
187      */
188     protected void process( NextInterceptor nextInterceptor, Modify call ) throws NamingException
189     {
190         // check if entry to modify exists
191         String msg = "Attempt to modify non-existant entry: ";
192         assertHasEntry( msg, call.getName() );
193 
194         nextInterceptor.process( call );
195     }
196 
197 
198     /***
199      * Checks to see the entry being modified exists, otherwise throws the appropriate LdapException.
200      */
201     protected void process( NextInterceptor nextInterceptor, ModifyMany call ) throws NamingException
202     {
203         // check if entry to modify exists
204         String msg = "Attempt to modify non-existant entry: ";
205         assertHasEntry( msg, call.getName() );
206 
207         nextInterceptor.process( call );
208     }
209 
210 
211     /***
212      * Checks to see the entry being renamed exists, otherwise throws the appropriate LdapException.
213      */
214     protected void process( NextInterceptor nextInterceptor, ModifyRN call ) throws NamingException
215     {
216         Name dn = call.getName();
217         String newRdn = call.getNewRelativeName();
218         
219         // check if entry to rename exists
220         String msg = "Attempt to rename non-existant entry: ";
221         assertHasEntry( msg, dn );
222 
223         // check to see if target entry exists
224         Name target = dn.getSuffix( 1 ).add( newRdn );
225         if ( nexus.hasEntry( target ) )
226         {
227             LdapNameAlreadyBoundException e = null;
228             e = new LdapNameAlreadyBoundException( "target entry " + target
229                     + " already exists!" );
230             e.setResolvedName( target );
231             throw e;
232         }
233 
234         nextInterceptor.process( call );
235     }
236 
237 
238     /***
239      * Checks to see the entry being moved exists, and so does its parent, otherwise throws the appropriate
240      * LdapException.
241      */
242     protected void process( NextInterceptor nextInterceptor, Move call ) throws NamingException
243     {
244         // check if child to move exists
245         String msg = "Attempt to move to non-existant parent: ";
246         assertHasEntry( msg, call.getName() );
247 
248         // check if parent to move to exists
249         msg = "Attempt to move to non-existant parent: ";
250         assertHasEntry( msg, call.getNewParentName() );
251 
252         // check to see if target entry exists
253         String rdn = call.getName().get( call.getName().size() - 1 );
254         Name target = ( Name ) call.getNewParentName().clone();
255         target.add( rdn );
256         if ( nexus.hasEntry( target ) )
257         {
258             LdapNameAlreadyBoundException e = null;
259             e = new LdapNameAlreadyBoundException( "target entry " + target
260                     + " already exists!" );
261             e.setResolvedName( target );
262             throw e;
263         }
264 
265         nextInterceptor.process( call );
266     }
267 
268 
269     /***
270      * Checks to see the entry being moved exists, and so does its parent, otherwise throws the appropriate
271      * LdapException.
272      */
273     protected void process( NextInterceptor nextInterceptor, MoveAndModifyRN call ) throws NamingException
274     {
275         // check if child to move exists
276         String msg = "Attempt to move to non-existant parent: ";
277         assertHasEntry( msg, call.getName() );
278 
279         // check if parent to move to exists
280         msg = "Attempt to move to non-existant parent: ";
281         assertHasEntry( msg, call.getNewParentName() );
282 
283         // check to see if target entry exists
284         Name target = ( Name ) call.getNewParentName().clone();
285         target.add( call.getNewRelativeName() );
286         if ( nexus.hasEntry( target ) )
287         {
288             LdapNameAlreadyBoundException e = null;
289             e = new LdapNameAlreadyBoundException( "target entry " + target
290                     + " already exists!" );
291             e.setResolvedName( target );
292             throw e;
293         }
294 
295         nextInterceptor.process( call );
296     }
297 
298 
299     /***
300      * Checks to see the entry being searched exists, otherwise throws the appropriate LdapException.
301      */
302     protected void process( NextInterceptor nextInterceptor, Search call ) throws NamingException
303     {
304         String msg = "Attempt to search under non-existant entry: ";
305 
306         Name base = call.getBaseName();
307         if ( base.size() == 0 )
308         {
309             nextInterceptor.process( call );
310             return;
311         }
312 
313         Attribute attr = nexus.getRootDSE().get( "subschemaSubentry" );
314         if ( ( ( String ) attr.get() ).equalsIgnoreCase( base.toString() ) )
315         {
316             nextInterceptor.process( call );
317             return;
318         }
319 
320         assertHasEntry( msg, base );
321 
322         nextInterceptor.process( call );
323     }
324 
325 
326     /***
327      * Asserts that an entry is present and as a side effect if it is not, creates a LdapNameNotFoundException, which is
328      * used to set the before exception on the invocation - eventually the exception is thrown.
329      *
330      * @param msg        the message to prefix to the distinguished name for explanation
331      * @param dn         the distinguished name of the entry that is asserted
332      * @throws NamingException if the entry does not exist
333      */
334     private void assertHasEntry( String msg, Name dn ) throws NamingException
335     {
336         if ( !nexus.hasEntry( dn ) )
337         {
338             LdapNameNotFoundException e = null;
339 
340             if ( msg != null )
341             {
342                 e = new LdapNameNotFoundException( msg + dn );
343             }
344             else
345             {
346                 e = new LdapNameNotFoundException( dn.toString() );
347             }
348 
349             e.setResolvedName( nexus.getMatchedDn( dn, false ) );
350             throw e;
351         }
352     }
353 }