package org.openntf.domino.xsp.xots; import java.lang.reflect.Constructor; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.concurrent.Callable; import org.eclipse.osgi.baseadaptor.loader.BaseClassLoader; import org.openntf.domino.config.Configuration; import org.openntf.domino.config.XotsConfiguration; import org.openntf.domino.thread.AbstractWrappedTask; import org.openntf.domino.types.Null; import org.openntf.domino.utils.DominoUtils; import org.openntf.domino.utils.Factory; import org.openntf.domino.utils.Factory.SessionType; import org.openntf.domino.xots.Tasklet; import org.openntf.domino.xsp.ODAPlatform; import com.ibm.commons.util.ThreadLock; import com.ibm.domino.xsp.module.nsf.NSFComponentModule; import com.ibm.domino.xsp.module.nsf.NotesContext; public class XotsWrappedTask extends AbstractWrappedTask { /** * Common code for the wrappers * * @param module * @param bubbleException * @param sessionFactory * @param callable * @param runnable * @return */ protected Object callOrRun(final NSFComponentModule module) throws Exception { NSFComponentModule codeModule = null; if (module != null) { codeModule = module.getTemplateModule() == null ? module : module.getTemplateModule(); if (module.isDestroyed() || codeModule.isDestroyed()) { throw new IllegalArgumentException("Module was destroyed in the meantime. Cannot run"); } module.updateLastModuleAccess(); codeModule.updateLastModuleAccess(); } final NotesContext ctx = new NotesContext(module); NotesContext.initThread(ctx); try { // checkme: What should we use here? //Factory.initThread(ODAPlatform.getAppThreadConfig(module.getNotesApplication())); Factory.initThread(sourceThreadConfig); try { return invokeTasklet(ctx, codeModule); } catch (Exception e) { DominoUtils.handleException(e); return null; } finally { Factory.termThread(); } } finally { NotesContext.termThread(); } } /** * Invokes the tasklet * * @param codeModule * @param bubbleException * @param sessionFactory * @param wrappedTask * @return * @throws Exception */ @SuppressWarnings("null") protected Object invokeTasklet(final NotesContext ctx, final NSFComponentModule module) throws Exception { ClassLoader mcl = null; ThreadLock readLock = null; NSFComponentModule codeModule = null; if (module == null) { mcl = getWrappedTask().getClass().getClassLoader(); } else { // RPr: In my opinion, This is the proper way how to run runnables in a different thread codeModule = module.getTemplateModule(); if (codeModule == null) codeModule = module; mcl = codeModule.getModuleClassLoader(); if (ODAPlatform.isAppFlagSet("LOCKXOTS", codeModule.getNotesApplication())) { readLock = XotsDominoExecutor.getLockManager(codeModule).getReadLock(); } } if (sessionFactory != null) { Factory.setSessionFactory(sessionFactory, SessionType.CURRENT); @SuppressWarnings("unused") org.openntf.domino.Session current = Factory.getSession(SessionType.CURRENT); } try { if (readLock != null) readLock.acquire(); // we want to read data from the module, so lock it! // set up the classloader ClassLoader oldCl = switchClassLoader(mcl); try { Object wrappedTask = getWrappedTask(); XotsDominoExecutor.initModule(ctx, mcl, wrappedTask); XotsConfiguration config = null; if (mcl instanceof BaseClassLoader) { // Determine the bundle of mcl String bundle = ((BaseClassLoader) mcl).getClasspathManager().getBaseData().getSymbolicName(); config = Configuration.getXotsBundleConfiguration(bundle, wrappedTask.getClass().getName()); } else { config = Configuration.getXotsNSFConfiguration(module.getDatabasePath(), wrappedTask.getClass().getName()); } try { config.logStart(); Object ret = invokeObject(wrappedTask); config.logSuccess(); return ret; } catch (Exception e) { config.logError(e); throw e; } } finally { switchClassLoader(oldCl); } } finally { if (readLock != null) readLock.release(); } } protected Object invokeObject(final Object wrappedTask) throws Exception { if (wrappedTask instanceof Callable) { return ((Callable<?>) wrappedTask).call(); } else { ((Runnable) wrappedTask).run(); return null; } } /** * Changes the Classloader and returns the old one * * @param codeModule * @return */ protected ClassLoader switchClassLoader(final ClassLoader newClassLoader) { return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() { @Override public ClassLoader run() { Thread thread = Thread.currentThread(); ClassLoader oldCl = thread.getContextClassLoader(); thread.setContextClassLoader(newClassLoader); return oldCl; } }); } /** * Finds the constructor for the given Tasklet * * @param clazz * @param args * @return */ protected Constructor<?> findConstructor(final Class<?> clazz, final Object[] args) { // sanity check if this is a public tasklet Tasklet annot = clazz.getAnnotation(Tasklet.class); if (annot == null) { throw new IllegalStateException("Cannot run " + clazz.getName() + ", because it does not annotate @Tasklet."); } // if (!(Callable.class.isAssignableFrom(clazz)) && !(Runnable.class.isAssignableFrom(clazz))) { // throw new IllegalStateException("Cannot run " + clazz.getName() + ", because it is no Runnable or Callable."); // } // find the constructor Class<?> ctorClasses[] = new Class<?>[args.length]; Object ctorArgs[] = new Object[args.length]; for (int i = 0; i < ctorClasses.length; i++) { Object arg; ctorArgs[i] = arg = args[i]; ctorClasses[i] = arg == null ? Null.class : arg.getClass(); } Constructor<?> cTor = null; try { cTor = clazz.getConstructor(ctorClasses); } catch (NoSuchMethodException nsme1) { try { cTor = clazz.getConstructor(new Class<?>[] { Object[].class }); ctorArgs = new Object[] { ctorArgs }; } catch (NoSuchMethodException nsme2) { } } if (cTor == null) { throw new IllegalStateException("Cannot run " + clazz.getName() + ", because it has no constructor for Arguments: " + ctorArgs); } return cTor; } }