You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by ak...@apache.org on 2006/01/06 04:26:38 UTC
svn commit: r366404 - in /directory/trunk:
apacheds-server-unit/src/test/java/org/apache/ldap/server/PersistentSearchTest.java
ldap-protocol/src/main/java/org/apache/ldap/server/protocol/support/SearchHandler.java
Author: akarasulu
Date: Thu Jan 5 19:26:32 2006
New Revision: 366404
URL: http://svn.apache.org/viewcvs?rev=366404&view=rev
Log:
changes ...
o implemented the returnEC field in the server so EntryChangeControls are sent
in responses if this field is set to true
o implemented selective notifications based on the changeTypes filed of the
PersistentSearchControl
o added test cases to test defaults, runs with returnEC set to true, and
tests confirming correct operation of the changeTypes field in PSearchCOntrol
Modified:
directory/trunk/apacheds-server-unit/src/test/java/org/apache/ldap/server/PersistentSearchTest.java
directory/trunk/ldap-protocol/src/main/java/org/apache/ldap/server/protocol/support/SearchHandler.java
Modified: directory/trunk/apacheds-server-unit/src/test/java/org/apache/ldap/server/PersistentSearchTest.java
URL: http://svn.apache.org/viewcvs/directory/trunk/apacheds-server-unit/src/test/java/org/apache/ldap/server/PersistentSearchTest.java?rev=366404&r1=366403&r2=366404&view=diff
==============================================================================
--- directory/trunk/apacheds-server-unit/src/test/java/org/apache/ldap/server/PersistentSearchTest.java (original)
+++ directory/trunk/apacheds-server-unit/src/test/java/org/apache/ldap/server/PersistentSearchTest.java Thu Jan 5 19:26:32 2006
@@ -26,9 +26,13 @@
import javax.naming.directory.DirContext;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.Control;
+import javax.naming.ldap.HasControls;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapContext;
+import org.apache.ldap.common.codec.search.controls.ChangeType;
+import org.apache.ldap.common.codec.search.controls.EntryChangeControl;
+import org.apache.ldap.common.codec.search.controls.EntryChangeControlDecoder;
import org.apache.ldap.common.message.PersistentSearchControl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -105,6 +109,9 @@
}
+ /**
+ * Shows correct notifications for modify(4) changes.
+ */
public void testPsearchModify() throws Exception
{
PSearchListener listener = new PSearchListener();
@@ -131,9 +138,14 @@
}
assertNotNull( listener.result );
+ // darn it getting normalized name back
+ assertEquals( RDN.toLowerCase(), listener.result.getName().toLowerCase() );
}
+ /**
+ * Shows correct notifications for moddn(8) changes.
+ */
public void testPsearchModifyDn() throws Exception
{
PSearchListener listener = new PSearchListener();
@@ -160,9 +172,14 @@
}
assertNotNull( listener.result );
+ // darn it getting normalized name back
+ assertEquals( "cn=Jack Black".toLowerCase(), listener.result.getName().toLowerCase() );
}
+ /**
+ * Shows correct notifications for delete(2) changes.
+ */
public void testPsearchDelete() throws Exception
{
PSearchListener listener = new PSearchListener();
@@ -189,9 +206,14 @@
}
assertNotNull( listener.result );
+ // darn it getting normalized name back
+ assertEquals( RDN.toLowerCase(), listener.result.getName().toLowerCase() );
}
+ /**
+ * Shows correct notifications for add(1) changes.
+ */
public void testPsearchAdd() throws Exception
{
PSearchListener listener = new PSearchListener();
@@ -218,17 +240,252 @@
}
assertNotNull( listener.result );
+ // darn it getting normalized name back
+ assertEquals( "cn=Jack Black".toLowerCase(), listener.result.getName().toLowerCase() );
}
+
+
+ /**
+ * Shows correct notifications for modify(4) changes with returned
+ * EntryChangeControl.
+ */
+ public void testPsearchModifyWithEC() throws Exception
+ {
+ PersistentSearchControl control = new PersistentSearchControl();
+ control.setReturnECs( true );
+ PSearchListener listener = new PSearchListener( control );
+ Thread t = new Thread( listener, "PSearchListener" );
+ t.start();
+ while( ! listener.isReady )
+ {
+ Thread.sleep( 100 );
+ }
+ Thread.sleep( 250 );
+
+ ctx.modifyAttributes( RDN, DirContext.REMOVE_ATTRIBUTE,
+ new BasicAttributes( "description", PERSON_DESCRIPTION, true ) );
+ long start = System.currentTimeMillis();
+ while ( t.isAlive() )
+ {
+ Thread.sleep( 200 );
+ if ( System.currentTimeMillis() - start > 3000 )
+ {
+ System.out.println( "PSearchListener thread not dead yet" );
+ break;
+ }
+ }
+
+ assertNotNull( listener.result );
+ // darn it getting normalized name back
+ assertEquals( RDN.toLowerCase(), listener.result.getName().toLowerCase() );
+ assertEquals( listener.result.control.getChangeType(), ChangeType.MODIFY );
+ }
+
+
+ /**
+ * Shows correct notifications for moddn(8) changes with returned
+ * EntryChangeControl.
+ */
+ public void testPsearchModifyDnWithEC() throws Exception
+ {
+ PersistentSearchControl control = new PersistentSearchControl();
+ control.setReturnECs( true );
+ PSearchListener listener = new PSearchListener( control );
+ Thread t = new Thread( listener );
+ t.start();
+
+ while( ! listener.isReady )
+ {
+ Thread.sleep( 100 );
+ }
+ Thread.sleep( 250 );
+
+ ctx.rename( RDN, "cn=Jack Black" );
+
+ long start = System.currentTimeMillis();
+ while ( t.isAlive() )
+ {
+ Thread.sleep( 100 );
+ if ( System.currentTimeMillis() - start > 3000 )
+ {
+ System.out.println( "PSearchListener thread not dead yet" );
+ break;
+ }
+ }
+
+ assertNotNull( listener.result );
+ // darn it getting normalized name back
+ assertEquals( "cn=Jack Black".toLowerCase(), listener.result.getName().toLowerCase() );
+ assertEquals( listener.result.control.getChangeType(), ChangeType.MODDN );
+ assertEquals( ( RDN + ",ou=system" ).toLowerCase(), listener.result.control.getPreviousDn().toLowerCase() );
+ }
+
+
+ /**
+ * Shows correct notifications for delete(2) changes with returned
+ * EntryChangeControl.
+ */
+ public void testPsearchDeleteWithEC() throws Exception
+ {
+ PersistentSearchControl control = new PersistentSearchControl();
+ control.setReturnECs( true );
+ PSearchListener listener = new PSearchListener( control );
+ Thread t = new Thread( listener );
+ t.start();
+
+ while( ! listener.isReady )
+ {
+ Thread.sleep( 100 );
+ }
+ Thread.sleep( 250 );
+
+ ctx.destroySubcontext( RDN );
+
+ long start = System.currentTimeMillis();
+ while ( t.isAlive() )
+ {
+ Thread.sleep( 100 );
+ if ( System.currentTimeMillis() - start > 3000 )
+ {
+ System.out.println( "PSearchListener thread not dead yet" );
+ break;
+ }
+ }
+
+ assertNotNull( listener.result );
+ // darn it getting normalized name back
+ assertEquals( RDN.toLowerCase(), listener.result.getName().toLowerCase() );
+ assertEquals( listener.result.control.getChangeType(), ChangeType.DELETE );
+ }
+
+ /**
+ * Shows correct notifications for add(1) changes with returned
+ * EntryChangeControl.
+ */
+ public void testPsearchAddWithEC() throws Exception
+ {
+ PersistentSearchControl control = new PersistentSearchControl();
+ control.setReturnECs( true );
+ PSearchListener listener = new PSearchListener( control );
+ Thread t = new Thread( listener );
+ t.start();
+
+ while( ! listener.isReady )
+ {
+ Thread.sleep( 100 );
+ }
+ Thread.sleep( 250 );
+
+ ctx.createSubcontext( "cn=Jack Black", getPersonAttributes( "Black", "Jack Black" ) );
+
+ long start = System.currentTimeMillis();
+ while ( t.isAlive() )
+ {
+ Thread.sleep( 100 );
+ if ( System.currentTimeMillis() - start > 3000 )
+ {
+ System.out.println( "PSearchListener thread not dead yet" );
+ break;
+ }
+ }
+
+ assertNotNull( listener.result );
+ // darn it getting normalized name back
+ assertEquals( "cn=Jack Black".toLowerCase(), listener.result.getName().toLowerCase() );
+ assertEquals( listener.result.control.getChangeType(), ChangeType.ADD );
+ }
+
+
+ /**
+ * Shows correct notifications for only add(1) and modify(4) registered changes with returned
+ * EntryChangeControl.
+ */
+ public void testPsearchAddModifyEnabledWithEC() throws Exception
+ {
+ PersistentSearchControl control = new PersistentSearchControl();
+ control.setReturnECs( true );
+ control.setChangeTypes( ChangeType.ADD_VALUE );
+ control.enableNotification( ChangeType.MODIFY );
+ PSearchListener listener = new PSearchListener( control );
+ Thread t = new Thread( listener );
+ t.start();
+
+ while( ! listener.isReady )
+ {
+ Thread.sleep( 100 );
+ }
+ Thread.sleep( 250 );
+
+ ctx.createSubcontext( "cn=Jack Black", getPersonAttributes( "Black", "Jack Black" ) );
+
+ long start = System.currentTimeMillis();
+ while ( t.isAlive() )
+ {
+ Thread.sleep( 100 );
+ if ( System.currentTimeMillis() - start > 3000 )
+ {
+ System.out.println( "PSearchListener thread not dead yet" );
+ break;
+ }
+ }
+
+ assertNotNull( listener.result );
+ // darn it getting normalized name back
+ assertEquals( "cn=Jack Black".toLowerCase(), listener.result.getName().toLowerCase() );
+ assertEquals( listener.result.control.getChangeType(), ChangeType.ADD );
+ listener.result = null;
+ t = new Thread( listener );
+ t.start();
+
+ ctx.destroySubcontext( "cn=Jack Black" );
+
+ start = System.currentTimeMillis();
+ while ( t.isAlive() )
+ {
+ Thread.sleep( 100 );
+ if ( System.currentTimeMillis() - start > 3000 )
+ {
+ System.out.println( "PSearchListener thread not dead yet" );
+ break;
+ }
+ }
+
+ assertNull( listener.result );
+
+ // thread is still waiting for notifications try a modify
+ ctx.modifyAttributes( RDN, DirContext.REMOVE_ATTRIBUTE,
+ new BasicAttributes( "description", PERSON_DESCRIPTION, true ) );
+ start = System.currentTimeMillis();
+ while ( t.isAlive() )
+ {
+ Thread.sleep( 200 );
+ if ( System.currentTimeMillis() - start > 3000 )
+ {
+ System.out.println( "PSearchListener thread not dead yet" );
+ break;
+ }
+ }
+
+ assertNotNull( listener.result );
+ // darn it getting normalized name back
+ assertEquals( RDN.toLowerCase(), listener.result.getName().toLowerCase() );
+ assertEquals( listener.result.control.getChangeType(), ChangeType.MODIFY );
+ }
+
+
class PSearchListener implements Runnable
{
boolean isReady = false;
- SearchResult result;
+ PSearchNotification result;
+ final PersistentSearchControl control;
+
+ PSearchListener() { control = new PersistentSearchControl(); }
+ PSearchListener( PersistentSearchControl control ) { this.control = control; }
public void run()
{
- PersistentSearchControl control = new PersistentSearchControl();
control.setCritical( true );
Control[] ctxCtls = new Control[] { control };
@@ -237,10 +494,30 @@
ctx.setRequestControls( ctxCtls );
isReady = true;
NamingEnumeration list = ctx.search( "", "objectClass=*", null );
+ EntryChangeControl ecControl = null;
+
while( list.hasMore() )
{
- result = ( SearchResult ) list.next();
- System.out.println( "got notifiaction for entry: " + result.getName() );
+ Control[] controls = null;
+ SearchResult sresult = ( SearchResult ) list.next();
+ if ( sresult instanceof HasControls )
+ {
+ controls = ( ( HasControls ) sresult ).getControls();
+ if ( controls != null )
+ {
+ for ( int ii = 0; ii < controls.length; ii ++ )
+ {
+ if ( controls[ii].getID().equals(
+ org.apache.ldap.common.message.EntryChangeControl.CONTROL_ID ) )
+ {
+ EntryChangeControlDecoder decoder = new EntryChangeControlDecoder();
+ ecControl = ( EntryChangeControl ) decoder.decode( controls[ii].getEncodedValue() );
+ }
+ }
+ }
+ }
+ result = new PSearchNotification( sresult, ecControl );
+ System.out.println( "got notifiaction: " + result );
break;
}
}
@@ -248,6 +525,33 @@
{
e.printStackTrace();
}
+ }
+ }
+
+
+ class PSearchNotification extends SearchResult
+ {
+ private static final long serialVersionUID = 1L;
+ final EntryChangeControl control;
+
+ public PSearchNotification( SearchResult result, EntryChangeControl control )
+ {
+ super( result.getName(), result.getClassName(), result.getObject(), result.getAttributes(), result.isRelative() );
+ this.control = control;
+ }
+
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer();
+ buf.append( getName() );
+ if ( control != null )
+ {
+ buf.append( "EntryChangeControl =\n" );
+ buf.append( " changeType : " ).append( control.getChangeType() ).append( "\n" );
+ buf.append( " previousDN : " ).append( control.getPreviousDn() ).append( "\n" );
+ buf.append( " changeNumber : " ).append( control.getChangeNumber() ).append( "\n" );
+ }
+ return buf.toString();
}
}
}
Modified: directory/trunk/ldap-protocol/src/main/java/org/apache/ldap/server/protocol/support/SearchHandler.java
URL: http://svn.apache.org/viewcvs/directory/trunk/ldap-protocol/src/main/java/org/apache/ldap/server/protocol/support/SearchHandler.java?rev=366404&r1=366403&r2=366404&view=diff
==============================================================================
--- directory/trunk/ldap-protocol/src/main/java/org/apache/ldap/server/protocol/support/SearchHandler.java (original)
+++ directory/trunk/ldap-protocol/src/main/java/org/apache/ldap/server/protocol/support/SearchHandler.java Thu Jan 5 19:26:32 2006
@@ -15,6 +15,7 @@
*/
package org.apache.ldap.server.protocol.support;
+
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
@@ -33,9 +34,11 @@
import javax.naming.event.ObjectChangeListener;
import javax.naming.ldap.LdapContext;
+import org.apache.ldap.common.codec.search.controls.ChangeType;
import org.apache.ldap.common.exception.LdapException;
import org.apache.ldap.common.filter.PresenceNode;
import org.apache.ldap.common.message.Control;
+import org.apache.ldap.common.message.EntryChangeControl;
import org.apache.ldap.common.message.LdapResultImpl;
import org.apache.ldap.common.message.PersistentSearchControl;
import org.apache.ldap.common.message.ReferralImpl;
@@ -79,11 +82,11 @@
SearchRequest req = ( SearchRequest ) request;
NamingEnumeration list = null;
- // check the attributes to see if a referral's ref attribute is included
String[] ids = null;
Collection retAttrs = new HashSet();
retAttrs.addAll( req.getAttributes() );
+ // check the attributes to see if a referral's ref attribute is included
if( retAttrs.size() > 0 && !retAttrs.contains( "ref" ) )
{
retAttrs.add( "ref" );
@@ -172,19 +175,6 @@
PersistentSearchControl psearchControl = getPersistentSearchControl( req );
if ( psearchControl != null )
{
- if ( psearchControl.isReturnECs() )
- {
- SearchResponseDone resp = new SearchResponseDoneImpl( req.getMessageId() );
- LdapResultImpl result = new LdapResultImpl( resp );
- resp.setLdapResult( result );
- result.setResultCode( ResultCodeEnum.UNAVAILABLECRITICALEXTENSION );
- String msg = "EntryChangeNotification response controls not implemented yet!";
- log.error( msg );
- result.setErrorMessage( msg );
- session.write( resp );
- return;
- }
-
PersistentSearchHandler handler = new PersistentSearchHandler( ctx, session, req );
StringBuffer buf = new StringBuffer();
req.getFilter().printToBuffer( buf );
@@ -541,6 +531,8 @@
final ServerLdapContext ctx;
final IoSession session;
final SearchRequest req;
+ final PersistentSearchControl control;
+
PersistentSearchHandler( ServerLdapContext ctx, IoSession session, SearchRequest req )
{
@@ -548,6 +540,7 @@
this.req = req;
this.ctx = ctx;
this.req.put( "PersistentSearchHandler", this );
+ this.control = getPersistentSearchControl( req );
}
@@ -645,25 +638,57 @@
private void sendEntry( NamingEvent evt )
{
+ /*
+ * @todo eventually you'll want to add the changeNumber once we move
+ * the CSN functionality into the server.
+ */
SearchResponseEntry respEntry = new SearchResponseEntryImpl( req.getMessageId() );
+ EntryChangeControl ecControl = null;
+ if ( control.isReturnECs() )
+ {
+ ecControl = new EntryChangeControl();
+ respEntry.add( ecControl );
+ }
+
switch ( evt.getType() )
{
case( NamingEvent.OBJECT_ADDED ):
+ if ( ! control.isNotificationEnabled( ChangeType.ADD ) ) return;
respEntry.setObjectName( evt.getNewBinding().getName() );
respEntry.setAttributes( ( Attributes ) evt.getChangeInfo() );
+ if ( ecControl != null )
+ {
+ ecControl.setChangeType( ChangeType.ADD );
+ }
break;
case( NamingEvent.OBJECT_CHANGED ):
+ if ( ! control.isNotificationEnabled( ChangeType.MODIFY ) ) return;
respEntry.setObjectName( evt.getOldBinding().getName() );
respEntry.setAttributes( ( Attributes ) evt.getOldBinding().getObject() );
+ if ( ecControl != null )
+ {
+ ecControl.setChangeType( ChangeType.MODIFY );
+ }
break;
case( NamingEvent.OBJECT_REMOVED ):
+ if ( ! control.isNotificationEnabled( ChangeType.DELETE ) ) return;
respEntry.setObjectName( evt.getOldBinding().getName() );
respEntry.setAttributes( ( Attributes ) evt.getOldBinding().getObject() );
+ if ( ecControl != null )
+ {
+ ecControl.setChangeType( ChangeType.DELETE );
+ }
break;
case( NamingEvent.OBJECT_RENAMED ):
- respEntry.setObjectName( evt.getOldBinding().getName() );
- respEntry.setAttributes( ( Attributes ) evt.getOldBinding().getObject() );
+ if ( ! control.isNotificationEnabled( ChangeType.MODDN ) ) return;
+ respEntry.setObjectName( evt.getNewBinding().getName() );
+ respEntry.setAttributes( ( Attributes ) evt.getNewBinding().getObject() );
+ if ( ecControl != null )
+ {
+ ecControl.setChangeType( ChangeType.MODDN );
+ ecControl.setPreviousDn( evt.getOldBinding().getName() );
+ }
break;
default:
return;