Hi Emmanuel, Thank you for replying to my question and providing a thorough explanation. I tried some recipes based on your response (with partial success) and that’s my observations.
1) I have to deal with some legacy code developed by a guy who left our company, so I’m trying not to break things and introduce very granular changes for the same purpose 2) Directory Server created explicitly with the following code as an example: DefaultDirectoryServiceFactory factory = new DefaultDirectoryServiceFactory() factory.init(“xxx.ldapgw") cs = new CacheService() cs.initialize( null) directoryService = factory.getDirectoryService() directoryService.setShutdownHookEnabled(true) directoryService.setAllowAnonymousAccess(ldapSettings.allowAnonymousAccess) if ( ldapSettings.schemaLdif.length() > 0 ) { def fSchema = new File(ldapSettings.schemaLdif) if ( !fSchema.exists()) { throw new Exception("LDIF file does not exist:" + ldapSettings.schemaLdif) } auditLog.info "Loading Schemas${new LdifFileLoader(directoryService.getAdminSession(), fSchema, null).execute()}" } directoryService.dnFactory = new DefaultDnFactory( directoryService.schemaManager, cs.getCache("dnCache")) // add our bind interceptor to the front of the list def interceptors = directoryService.getInterceptors() interceptors.add(0, bindInterceptor) // interceptors.add(findNormalizationInterceptorPosition(interceptors) + 1, crudInterceptor) bindInterceptor.init(directoryService) directoryService.setInterceptors(interceptors) directoryService.addFirst(crudInterceptor) def adminSession = directoryService.getAdminSession() ldapServer = new LdapServer() ldapServer.directoryService = directoryService ... So it’s not declarative like it’s shown in the online documentation, which I have to admit is quite outdated. Maybe there is a place where it’s in the most recent state? As you can see from the file, there is a section related to interceptors, and while setting server up, I see that my interceptors is in the middle of the pack. I can also see that while doing “modify(opContext)” in my custom interceptor, where opContext would reflect total number of interceptors and current number (though still throwing NPE at next(opContext)). Nevertheless, I tried to grab the default config.ldif from downloaded DS archive and inserted a section related to interceptors into schemaLdif file. That didn’t really change anything for me. I think it might be related to the fact that I can’t see anything with Directory Browser under ou=config, after server is up. So would be really nice if you can direct me to how I can use config.ldif while setting the server up programmatically. 2) As you can see from the snippet, I tried to do .addFirst(crudInterceptor) and from a first glance it worked for me, no more NPE, my custom code works fine, but as you said, I’m not sure if that’s a right way or any complications can be right around the corner. Please let me know your thoughts on those two topics. Thank you, Philipp =================== Accordingly to what you posted on stackoverflow, the NPE is due to the fact your interceptor does not know what is the next one to call: protected Interceptor getNextInterceptor( OperationContext operationContext ) { String currentInterceptor = operationContext.getNextInterceptor(); if ( currentInterceptor.equals( "FINAL" ) ) Here, currentInterceptor is most certainly null. Why is that ? The operationContext (here, it's an instance of ModifyOperationContext) contains a list of interceptors to call, and this list is computed when the server is started by the DefaultDirectoryServer.gatherInterceptors() private method. This method gets all the interceptors that are configured, and for each operation context, search for the associated method using reflection (here, it willl search for the 'modify' method). At the end, each OperationContext instance is stored in a Map associated with the list of interceptors it may call ( this map is named operationInterceptors). When a new operation starts (ie when a user send a request to the server), it will get the list of interceptors to process from this map (which can be modified dynamically, but that's another story). Anyway, the problem here is to be able to inject another interceptor and to have it properly 'linked' so that when the operation fetch the next interceptor, it finds it. This is all about registering the interceptor. Currently, this is done by this method: private void addInterceptor( Interceptor interceptor, int position ) It adds an interceptor at a given position. The thing is that this method is private, so it can't be called outside the DefaultDirectoryServer class. It's called at two other places: - addFirst - addLast which are public methods. That means the only way to inject an interceptor into the chain is to call one of those two methods (and as it only add an interceptor at the end or at the beginning of the list, it must be called exactly at the right moment accordingly to the expected position of the new interceptor in the chain. It also means you don't really have a way to inject your interceptor in the middle of the chain, because you don't have a way to control how this chain is created (at least programatically). The key here is to play with the server configuration. When the server starts, it reads the config from a set of LDIF files and extract the list of intercpeots - and the order in which they will be loaded - from this configuration. It then creates a list of those interceptors, pass it to the DefaultDirectoryServer instance using the setInterceptors() method, then the initOperationsList() method is called, which create the Map we mention before, with the proper intercaptor lists for each operation. So you have to configure your system with a new LDIF like this one : dn: ads-interceptorId=|MyCRUDInterceptor|,ou=interceptors,ads-directoryServiceId=default,ou=config objectclass: top objectclass: ads-base objectclass: ads-interceptor ads-interceptororder: <NNN> ads-interceptorclassname: xxx.company.ldapbridge.impl.MyCRUDInterceptor ads-interceptorid: |MyCRUDInterceptor| ads-enabled: TRUE Note that the ads-interceptororder value (here, <NNN>) must be consistent with the other interceptors' position, which means you will have to change all the other orders if you inject your interceptor in the middle. Ok, this is how it works. It's not simple, and I hope I have been clear enough... Please ask if you have any issue with what I explained ! > > I posted a question on StackOverflow with details of my implementation and a > stack trace. Not sure if I need to duplicate everything here, but will do if required. It’s my first time posting into this mail list. > > Here is the URL for my question on SO -> > https://stackoverflow.com/questions/57150901/calling-nextmodifycontext-from-a-custom-interceptor-leads-to-nullpointerexcept <https://stackoverflow.com/questions/57150901/calling-nextmodifycontext-from-a-custom-interceptor-leads-to-nullpointerexcept> > > I’d appreciate any help or thoughts.