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.ldap.handlers; 021 022 023 import javax.naming.NamingException; 024 025 import org.apache.directory.server.core.event.DirectoryListener; 026 import org.apache.directory.server.core.interceptor.context.AddOperationContext; 027 import org.apache.directory.server.core.interceptor.context.ChangeOperationContext; 028 import org.apache.directory.server.core.interceptor.context.DeleteOperationContext; 029 import org.apache.directory.server.core.interceptor.context.ModifyOperationContext; 030 import org.apache.directory.server.core.interceptor.context.MoveAndRenameOperationContext; 031 import org.apache.directory.server.core.interceptor.context.MoveOperationContext; 032 import org.apache.directory.server.core.interceptor.context.RenameOperationContext; 033 import org.apache.directory.server.i18n.I18n; 034 import org.apache.directory.server.ldap.LdapSession; 035 import org.apache.directory.shared.ldap.codec.search.controls.ChangeType; 036 import org.apache.directory.shared.ldap.codec.search.controls.entryChange.EntryChangeControl; 037 import org.apache.directory.shared.ldap.codec.search.controls.persistentSearch.PersistentSearchControl; 038 import org.apache.directory.shared.ldap.message.AbandonListener; 039 import org.apache.directory.shared.ldap.message.SearchResponseEntryImpl; 040 import org.apache.directory.shared.ldap.message.internal.InternalAbandonableRequest; 041 import org.apache.directory.shared.ldap.message.internal.InternalSearchRequest; 042 import org.apache.directory.shared.ldap.message.internal.InternalSearchResponseEntry; 043 import org.slf4j.Logger; 044 import org.slf4j.LoggerFactory; 045 046 047 /** 048 * A DirectoryListener implementation which sends back added, deleted, modified or 049 * renamed entries to a client that created this listener. This class is part of the 050 * persistent search implementation which uses the event notification scheme built into 051 * the server core. 052 * 053 * This listener is disabled only when a session closes or when an abandon request 054 * cancels it. Hence time and size limits in normal search operations do not apply 055 * here. 056 * 057 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 058 * @version $Rev: 905344 $ 059 */ 060 public class PersistentSearchListener implements DirectoryListener, AbandonListener 061 { 062 private static final Logger LOG = LoggerFactory.getLogger( PersistentSearchListener.class ); 063 final LdapSession session; 064 final InternalSearchRequest req; 065 final PersistentSearchControl control; 066 067 068 PersistentSearchListener( LdapSession session, InternalSearchRequest req ) 069 { 070 this.session = session; 071 this.req = req; 072 req.addAbandonListener( this ); 073 this.control = ( PersistentSearchControl ) req.getControls().get( PersistentSearchControl.CONTROL_OID ); 074 } 075 076 077 public void abandon() throws NamingException 078 { 079 // must abandon the operation 080 session.getCoreSession().getDirectoryService().getEventService().removeListener( this ); 081 082 /* 083 * From RFC 2251 Section 4.11: 084 * 085 * In the event that a server receives an Abandon Request on a Search 086 * operation in the midst of transmitting responses to the Search, that 087 * server MUST cease transmitting entry responses to the abandoned 088 * request immediately, and MUST NOT send the SearchResultDone. Of 089 * course, the server MUST ensure that only properly encoded LDAPMessage 090 * PDUs are transmitted. 091 * 092 * SO DON'T SEND BACK ANYTHING!!!!! 093 */ 094 } 095 096 097 public void requestAbandoned( InternalAbandonableRequest req ) 098 { 099 try 100 { 101 abandon(); 102 } 103 catch ( NamingException e ) 104 { 105 LOG.error( I18n.err( I18n.ERR_164 ), e ); 106 } 107 } 108 109 110 private void setECResponseControl( InternalSearchResponseEntry 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 InternalSearchResponseEntry 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 InternalSearchResponseEntry 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 InternalSearchResponseEntry respEntry = new SearchResponseEntryImpl( req.getMessageId() ); 170 respEntry.setObjectName( opContext.getDn() ); 171 respEntry.setEntry( opContext.getAlteredEntry() ); 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 InternalSearchResponseEntry 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 InternalSearchResponseEntry 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 }