To use the approach as an alternative to Domino’s HTTP task, we need support for the different user contexts, because using NotesFactory.createSession() just creates a session for the current Notes ID used.
This goal can be achived by using the Java NAPI and the following method:
import com.ibm.domino.napi.NException;
import com.ibm.domino.napi.c.NotesUtil;
import com.ibm.domino.napi.c.xsp.XSPNative;
/**
* create a new Domino session for the give username
* @param userName
* String containing the canonical username
* @return
* lotus.domino.Session for the given username
* @throws NException
* @throws ServletException
*/
public static Session createUserSession(final String userName)
{
Session session = null;
try {
long hList = NotesUtil.createUserNameList(userName);
session = XSPNative.createXPageSession(userName, hList,
false, false);
return session;
} catch (Exception e) {
e.printStackTrace();
}
return session;
}
It is required to load the required njnotes.dll before this method can be used, otherwise the C-API references won’t work. This must be done after initiation of the NotesThread and (only once) per thread:
System.loadLibrary("njnotes");
It is also required to use the full hierarchical name for the session creation, otherwise readers / authors won’t work correctly (you can create a session for every user you want, even for not existing ones).
To get this work correctly, I have created a little helper class which memorizes if the Notes-relevant part was already initialized for the thread used:
public class Utils {
private static final ThreadLocal<Boolean> isNotesInitialized = new ThreadLocal<Boolean>();
public static void initNotes(){
// check if the Thread is already initialized
if( Boolean.TRUE.equals( isNotesInitialized.get() ) )
return;
// init Notes
NotesThread.sinitThread();
System.loadLibrary("njnotes");
// mark as initialized
isNotesInitialized.set( Boolean.TRUE );
}
}
Now let’s activate Spring Boot Security on the new server by adding the required dependencies to the pom.xml:
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
To use the authentication with the existing Domino environment, we can use our own AuthenticationProvider:
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.stereotype.Component;
import org.springframework.security.authentication.BadCredentialsException;
@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String name = authentication.getName();
String password = authentication.getCredentials().toString();
String realUser = validatePassword( name, password );
if( Utils.isEmptyString( realUser ) ){
throw new BadCredentialsException("Authentication failed for user = " + name);
}
return auth;
}
@Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
public String validatePassword( final String userName, final String password){
// see https://github.com/hasselbach/domino-stateless-token-servlet/blob/master/src/ch/hasselba/servlet/DominoStatelessTokenServlet.java
// and convert the username to the hierarchical one
}
}
If the authentication was successfull, we can access the correct username from the Principal object. Just add it as a parameter to the controller method, and you get the hierarchical name.
@MessageMapping("/hello")
@SendTo("/topic/greetings")
public Greeting greeting(HelloMessage message, Principal principal) throws Exception {
Thread.sleep(1000); // simulated delay
// init Notes
Utils.initNotes();
// create the session for the user
Session session = (Session) Utils.createUserSession(principal.getName());
return new Greeting("Hello, " + HtmlUtils.htmlEscape( session.getEffectiveUserName() ) + "!" );
}
Now let’s see this in action:
P.S.
I have ignored recycling and termination of the Notes threads for an easier understanding.
Hello Sven,
very cool. Is it also possible to use the OpenNTF Domino API (https://oda.openntf.org/)?
best regards
Markus
Sure. You have the full JVM of Domino under your control.
There is a non-XSP package of ODA. Daniele Vistalli and I used that previously for CrossWorlds.
After I recycle the session, I also call
com.ibm.domino.napi.c.Os.OSUnlock(hList);
com.ibm.domino.napi.c.Os.OSMemFree(hList);
to free up the memory for the NAMES_LIST structure. I guess you don’t yet because the hList is a private variable in the createUserSession method.
I never looked as deep into the hList and the memory consumtion, I never had problems here even with thousands of requests per minute in monthes long usage in productive environment… 🙂 But thanks for the hint!