/* * DominoRunner version 1.0 - 20150915 * * By Serdar Basegmez (@sbasegmez), Developi * http://lotusnotus.com/en * * This class is a temporary encapsulation for a code that needs a Session. I have inspired from a number of snippets, * but probably, Nathan T. Freeman is the founder of the idea itself :) * * This code has been tested on; * * - DOTS tasklets, * - XPages apps, * - Apache Wink servlets, * - OSGi-level threads (specifically for HttpService) * * Although I've been using this for a couple of months in a production system, still, it's quite out of the mainstream * and it contains some bad practices. So use it at your own risk :) * * Sometimes, developing Java for Domino applications, we need a session for a quick operation. This class will try to * get the Session from NotesContext (XSP Session provider) and ContextInfo (Servlet session provider) respectively. * If it can't get the session, it would mean that we are out of these contexts (maybe in a standalone app, or DOTS * tasklet, etc.). Then it will try to start a NotesThread and terminate it after our code. * * Since it uses the reflection mechanism, it does not introduce any additional dependency to the project. * * A quick sample is included as a method below... * */ import java.lang.reflect.Method; import java.security.AccessController; import java.security.PrivilegedAction; import lotus.domino.NotesException; import lotus.domino.NotesFactory; import lotus.domino.NotesThread; import lotus.domino.Session; public class DominoRunner { public interface SessionRoutine<T> { public T doRun(Session session); public T fallback(); public T onException(Throwable t); } /* * Runs the routine with a Session. * * If trusted is true: it uses a server/signer session, otherwise uses the current user session. * */ public static <T> T runWithSession(boolean trusted, SessionRoutine<T> routine) { // We need a session. So first, we'll try to get the session from the NotesContext. // If the caller eventually binded to an XPages session, we'll be able to grab a session. // The only problem is that; session coming from NotesContext will be the SignerSession. // Hope there is only one almighty developer :) Session session=null; session = findNotesContextSession(trusted); if(null != session) { // We Got a NotesContext session! try { return routine.doRun(session); } catch(Throwable t) { return routine.onException(t); } } // So we couldn't grab the NotesContext session... It means that we are out of XPages. // If this is a servlet, we still have a chance for getting a User Session. // However if a trusted session requested, ContextInfo will not provide one. if(!trusted) { session = findContextInfoSession(); if(null != session) { // We got a User Session from the ContextInfo try { return routine.doRun(session); } catch(Throwable t) { return routine.onException(t); } } } // We might be on an OSGi-level thread, DOTS or a servlet. // Either way, we hope we are allowed to have NotesThread session. // As far as we know, NotesThread is forbidden in XPages threads. try { NotesThread.sinitThread(); session = NotesFactory.createTrustedSession(); if(null != session) { // Got a session! try { return routine.doRun(session); } catch(Throwable t) { return routine.onException(t); } } // Session is null... } catch (NotesException e) { // Ooops. We can't have a Session. That means trouble. } finally { NotesThread.stermThread(); if(session != null) { try { session.recycle(); // Not sure if it's necessary? } catch (NotesException e) {} } } // No other option for now. We can't have a valid Session... return routine.fallback(); } /** * We will try to get a session from the NotesContext. We use Reflection to avoid any dependencies and * ClassNotFound errors. * * This is the way XSP Engine works for now, this would be changed in the future (IBM says so in the ExtLib code). */ private static Session findNotesContextSession(final boolean signer) { return AccessController.doPrivileged(new PrivilegedAction<Session>() { @Override public Session run() { try { // This classloader is not available in Wink. // Wink servlets replaces classloaders during runtime. ClassLoader cl = Thread.currentThread().getContextClassLoader(); Class<?> clazz = cl.loadClass("com.ibm.domino.xsp.module.nsf.NotesContext"); Method m1 = clazz.getDeclaredMethod("getCurrentUnchecked", new Class[0]); Method m2; if(signer) { m2 = clazz.getDeclaredMethod("getSessionAsSignerFullAdmin", new Class[0]); } else { m2 = clazz.getDeclaredMethod("getCurrentSession", new Class[0]); } Object nc = m1.invoke(null, new Object[0]); if(nc==null) { // NotesContext is unavailable. We are out of XSP context. return null; } else { return (Session) m2.invoke(nc, new Object[0]); } } catch (ClassNotFoundException e) { // We couldn't find the class. return null; } catch (NoClassDefFoundError e) { // We couldn't access the class. return null; } catch (Exception e) { // Unhandled exception return null; } } }); } /** * We will try to get a session from the ContextInfo. We use Reflection to avoid any dependencies and * ClassNotFound errors. */ private static Session findContextInfoSession() { return AccessController.doPrivileged(new PrivilegedAction<Session>() { @Override public Session run() { try { // This classloader is not available in Wink. // Wink servlets replaces classloaders during runtime. ClassLoader cl = Thread.currentThread().getContextClassLoader(); Class<?> clazz = cl.loadClass("com.ibm.domino.osgi.core.context.ContextInfo"); Method m1 = clazz.getDeclaredMethod("getUserSession", new Class[0]); return (Session) m1.invoke(null, new Object[0]); } catch (ClassNotFoundException e) { // We couldn't find the class. } catch (NoClassDefFoundError e) { // We couldn't access the class. } catch (Throwable t) { // Unhandled exception } return null; } }); } public static void aQuickSample() { String effectiveUserName = DominoRunner.runWithSession(false, new SessionRoutine<String>() { @Override public String doRun(Session session) { try { return session.getEffectiveUserName(); } catch (NotesException e) { e.printStackTrace(); return null; } } @Override public String fallback() { System.out.println("Unable to get a Session... No backup plan... Doomed!"); return null; } @Override public String onException(Throwable t) { System.out.println("Exception, while getting a Session..."); t.printStackTrace(); return null; } }); if (effectiveUserName != null) { System.out.println("Effective User Name: "+effectiveUserName); } else { System.out.println("Unable to find a user name..."); } } }