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.ldap.handlers;
21  
22  
23  import javax.naming.NamingException;
24  
25  import org.apache.directory.server.core.event.DirectoryListener;
26  import org.apache.directory.server.core.interceptor.context.AddOperationContext;
27  import org.apache.directory.server.core.interceptor.context.ChangeOperationContext;
28  import org.apache.directory.server.core.interceptor.context.DeleteOperationContext;
29  import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
30  import org.apache.directory.server.core.interceptor.context.MoveAndRenameOperationContext;
31  import org.apache.directory.server.core.interceptor.context.MoveOperationContext;
32  import org.apache.directory.server.core.interceptor.context.RenameOperationContext;
33  import org.apache.directory.server.ldap.LdapSession;
34  import org.apache.directory.shared.ldap.codec.search.controls.ChangeType;
35  import org.apache.directory.shared.ldap.message.AbandonListener;
36  import org.apache.directory.shared.ldap.message.AbandonableRequest;
37  import org.apache.directory.shared.ldap.message.EntryChangeControl;
38  import org.apache.directory.shared.ldap.message.PersistentSearchControl;
39  import org.apache.directory.shared.ldap.message.SearchRequest;
40  import org.apache.directory.shared.ldap.message.SearchResponseEntry;
41  import org.apache.directory.shared.ldap.message.SearchResponseEntryImpl;
42  
43  import org.slf4j.Logger;
44  import org.slf4j.LoggerFactory;
45  
46  
47  /**
48   * A DirectoryListener implementation which sends back added, deleted, modified or 
49   * renamed entries to a client that created this listener.  This class is part of the
50   * persistent search implementation which uses the event notification scheme built into
51   * the server core.  
52   * 
53   * This listener is disabled only when a session closes or when an abandon request 
54   * cancels it.  Hence time and size limits in normal search operations do not apply
55   * here.
56   * 
57   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
58   * @version $Rev: 690033 $
59   */
60  public class PersistentSearchListener implements DirectoryListener, AbandonListener
61  {
62      private static final Logger LOG = LoggerFactory.getLogger( PersistentSearchListener.class );
63      final LdapSession session;
64      final SearchRequest req;
65      final PersistentSearchControl control;
66  
67  
68      PersistentSearchListener( LdapSession session, SearchRequest req )
69      {
70          this.session = session;
71          this.req = req;
72          req.addAbandonListener( this );
73          this.control = ( PersistentSearchControl ) req.getControls().get( PersistentSearchControl.CONTROL_OID );
74      }
75  
76  
77      public void abandon() throws NamingException
78      {
79          // must abandon the operation 
80          session.getCoreSession().getDirectoryService().getEventService().removeListener( this );
81  
82          /*
83           * From RFC 2251 Section 4.11:
84           * 
85           * In the event that a server receives an Abandon Request on a Search  
86           * operation in the midst of transmitting responses to the Search, that
87           * server MUST cease transmitting entry responses to the abandoned
88           * request immediately, and MUST NOT send the SearchResultDone. Of
89           * course, the server MUST ensure that only properly encoded LDAPMessage
90           * PDUs are transmitted. 
91           * 
92           * SO DON'T SEND BACK ANYTHING!!!!!
93           */
94      }
95  
96      
97      public void requestAbandoned( AbandonableRequest req )
98      {
99          try
100         {
101             abandon();
102         }
103         catch ( NamingException e )
104         {
105             LOG.error( "failed to properly abandon this persistent search", e );
106         }
107     }
108     
109     
110     private void setECResponseControl( SearchResponseEntry response, ChangeOperationContext opContext, ChangeType type )
111     {
112         if ( control.isReturnECs() )
113         {
114             EntryChangeControl ecControl = new EntryChangeControl();
115             ecControl.setChangeType( type );
116             
117             if ( opContext.getChangeLogEvent() != null )
118             {
119                 ecControl.setChangeNumber( opContext.getChangeLogEvent().getRevision() );
120             }
121          
122             if ( opContext instanceof RenameOperationContext || opContext instanceof MoveOperationContext )
123             {
124                 ecControl.setPreviousDn( opContext.getDn() ); 
125             }
126             
127             response.add( ecControl );
128         }
129     }
130 
131 
132     public void entryAdded( AddOperationContext opContext )
133     {
134         if ( ! control.isNotificationEnabled( ChangeType.ADD ) )
135         {
136             return;
137         }
138     
139         SearchResponseEntry respEntry = new SearchResponseEntryImpl( req.getMessageId() );
140         respEntry.setObjectName( opContext.getDn() );
141         respEntry.setEntry( opContext.getEntry() );
142         setECResponseControl( respEntry, opContext, ChangeType.ADD );
143         session.getIoSession().write( respEntry );
144     }
145 
146 
147     public void entryDeleted( DeleteOperationContext opContext )
148     {
149         if ( ! control.isNotificationEnabled( ChangeType.DELETE ) )
150         {
151             return;
152         }
153     
154         SearchResponseEntry respEntry = new SearchResponseEntryImpl( req.getMessageId() );
155         respEntry.setObjectName( opContext.getDn() );
156         respEntry.setEntry( opContext.getEntry() );
157         setECResponseControl( respEntry, opContext, ChangeType.DELETE );
158         session.getIoSession().write( respEntry );
159     }
160 
161 
162     public void entryModified( ModifyOperationContext opContext )
163     {
164         if ( ! control.isNotificationEnabled( ChangeType.MODIFY ) )
165         {
166             return;
167         }
168     
169         SearchResponseEntry respEntry = new SearchResponseEntryImpl( req.getMessageId() );
170         respEntry.setObjectName( opContext.getDn() );
171         respEntry.setEntry( opContext.getEntry() );
172         setECResponseControl( respEntry, opContext, ChangeType.MODIFY );
173         session.getIoSession().write( respEntry );
174     }
175 
176 
177     public void entryMoved( MoveOperationContext opContext )
178     {
179         if ( ! control.isNotificationEnabled( ChangeType.MODDN ) )
180         {
181             return;
182         }
183     
184         SearchResponseEntry respEntry = new SearchResponseEntryImpl( req.getMessageId() );
185         respEntry.setObjectName( opContext.getDn() );
186         respEntry.setEntry( opContext.getEntry() );
187         setECResponseControl( respEntry, opContext, ChangeType.MODDN );
188         session.getIoSession().write( respEntry );
189     }
190 
191 
192     public void entryMovedAndRenamed( MoveAndRenameOperationContext opContext )
193     {
194         entryRenamed( opContext );
195     }
196 
197 
198     public void entryRenamed( RenameOperationContext opContext )
199     {
200         if ( ! control.isNotificationEnabled( ChangeType.MODDN ) )
201         {
202             return;
203         }
204     
205         SearchResponseEntry respEntry = new SearchResponseEntryImpl( req.getMessageId() );
206         respEntry.setObjectName( opContext.getAlteredEntry().getDn() );
207         respEntry.setEntry( opContext.getAlteredEntry() );
208         setECResponseControl( respEntry, opContext, ChangeType.MODDN );
209         session.getIoSession().write( respEntry );
210     }
211 }