XPage并发NotesContext未初始化

时间:2013-11-29 12:31:43

标签: multithreading concurrency xpages lotus-notes

Domino版本:9.0.1
设计师版本:9.0.1
ExtLibs:901v00_01.20131029-1400

我正在使用this site as a source of information.

用例/情境
我有一个XPage应用程序,我希望允许用户按下按钮来启动并发任务(比如使用配置文档创建复杂文档等)。我不希望在创建这些文档所花费的时间内阻止UI线程。我还希望通过创建抽象类来为将来的XPage应用程序提供此选项,以便以后更轻松。

我正在使用上面列出的网站上提供的@stwissel源代码。这是我在google上发现XPage并发性的最佳起点,因为这是一种抽象的未来友好的设置类的方法。如果读者希望我将源代码放在问题上,我可以编辑问题并执行此操作。

问题
正如我在该网站的评论部分所述,我在获取当前上下文时遇到问题,以便可以获取会话和数据库等。

29.11.2013 12:47:22   HTTP JVM: Exception in thread "pool-2-thread-1" 
29.11.2013 12:47:22   HTTP JVM: java.lang.IllegalStateException: NotesContext not initialized for the thread
29.11.2013 12:47:22   HTTP JVM:     at com.ibm.domino.xsp.module.nsf.NotesContext.getCurrent(NotesContext.java:123)
29.11.2013 12:47:22   HTTP JVM:     at com.ibm.domino.xsp.module.nsf.ModuleClassLoader$DynamicClassLoader.loadClass(ModuleClassLoader.java:383)
29.11.2013 12:47:22   HTTP JVM:     at java.lang.ClassLoader.loadClass(ClassLoader.java:638)
29.11.2013 12:47:22   HTTP JVM:     at de.holistic.utils.multithreading.AbstractBackgroundTask.run(AbstractBackgroundTask.java:29)
29.11.2013 12:47:22   HTTP JVM:     at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:906)
29.11.2013 12:47:22   HTTP JVM:     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:929)
29.11.2013 12:47:22   HTTP JVM:     at java.lang.Thread.run(Thread.java:738)

同样,我只使用链接网站上的代码,我正在测试的任务只是一个简单的“创建文档”类测试线程,没什么特别的。

private void runNotes(){ 

        Database events = null;
        Document registration = null;

        try{
            events = notesSession.getCurrentDatabase();//notesSession is apart of the abstract class
            registration = events.createDocument();
            registration.replaceItemValue("Form", "frm_testDoc");
            registration.replaceItemValue("testField", "this is a test");
            registration.save();

        } catch (NotesException e) {
            OpenLogItem.logError(e);
        } finally {
            if(registration != null) try{registration.recycle();} catch (NotesException e) {}
            if(events != null) try{events.recycle();} catch (NotesException e) {}
        }
}

如果有人能帮我一把,我将非常感激。谢谢!

编辑 - 包含更多代码

这是作为会话bean的线程管理代码。它根本没有改变。

public class ThreadManager implements Serializable {
    private static final long serialVersionUID  = 1L;
    // # of threads running concurrently
    private static final int THREADPOOLSIZE = 10;
    private final ExecutorService service = Executors.newFixedThreadPool(THREADPOOLSIZE);

    public ThreadManager() {
        // For use in managed beans
        System.out.println("creating HolisticThreadmanager"); //TODO delete
    }

    public void submitService(final Runnable taskDef) {
        if (taskDef == null) {
            System.err.println("submitService: NULL callable submitted to submitService");
            return;
        }

        this.service.execute(taskDef);
    }

    public boolean test(){ // TODO delete
        System.out.println("Test successful");
        return true;
    }

    @Override
    protected void finalize() throws Throwable {
        if ((this.service != null) && !this.service.isTerminated()) {
            this.service.shutdown();
        }
        super.finalize();
    }
}

这是一个抽象类,后来经过一些改动,添加了更多的服务器日志打印来解决问题。我还将会话变量从private更改为protected,因此我可以从runNotes()方法访问会话。

public abstract class AbstractBackgroundTask implements Runnable {
    protected final Session notesSession;
    protected final Collection<String> callBackMessages;
    private SessionCloner sessionCloner;
    private NSFComponentModule module;

    protected Session session;

    public AbstractBackgroundTask(final Session optionalSession, final Collection<String> messageHandler) {
        this.callBackMessages = messageHandler;
        // optionalSession MUST be NULL when this should run in a thread, contain a session when
        // the class is running in the same thread as it was constructed
        this.notesSession = optionalSession;
        this.setDominoContextCloner();
    }


    public void run() {
        System.out.println("in run!"); //TODO erase
        this.callBackMessages.add("Background task run starting: " + this.getClass().toString());
        try { 
            System.out.println("about to ask if notesSession is null"); //TODO erase
            if (this.notesSession == null) {
                System.out.println("notesSession is null!"); //TODO erase
                if(this.module == null){
                    System.out.println("the module is null!!!");
                }
                NotesContext context = new NotesContext(this.module);
                NotesContext.initThread(context);
                session = this.sessionCloner.getSession();
            } else {
                // We run in an established session
                System.out.println("session established!"); //TODO erase
                session = this.notesSession;
            }
            System.out.println("about to start runNotes()"); //TODO delete
            /* Do the work here */
            this.runNotes();

        } catch (Throwable e) {
            System.out.println("in the run stack trace!"); //TODO delete
            System.out.println(e.getMessage());
        } finally {
            if (this.notesSession == null) {
                NotesContext.termThread();
                try {
                    this.sessionCloner.recycle();
                } catch (NotesException e1) {
                    System.out.println(e1.getStackTrace());
                }
            }
        }
        this.callBackMessages.add("Background task run completed: " + this.getClass().toString());
    }

    private void setDominoContextCloner() {
        // Domino stuff to be able to get a cloned session
        System.out.println("in cloner()"); //TODO erase
        if (this.notesSession == null) {
            try {
                NotesContext ncontext = NotesContext.getCurrent();
                if(ncontext == null){
                    System.out.println("ncontext is null!"); //TODO erase
                    OpenLogItem.logError(new OpenLogDummyException("NotesContext is null"));
                }
                this.module = ncontext.getModule();
                if(module == null){
                    OpenLogItem.logError(new OpenLogDummyException("Module is null"));
                    System.out.println("module is null");
                }
                this.sessionCloner = SessionCloner.getSessionCloner();
            } catch (Exception e) {
                OpenLogItem.logError(e);
                System.out.println(e.getStackTrace());
            }
        }
    }

    protected abstract void runNotes();
}

工人阶级:

public class Registrator extends AbstractBackgroundTask implements Serializable{

    /**
     * 
     */
    private static final long serialVersionUID = 4136706821621738665L;
    private final String crmDbServer = "MyServer/MyCompany/DE";
    private final String crmDbPath = "directory1\\datenbank.nsf";
    private ArrayList<String> messageFromThread = new ArrayList<String>();



    public Registrator() {
        super(null, messagesFromThread);
        System.out.println("in Registrator.const"); // TODO erase
        this.fromCRM = fromCRM;
    }

    @Override
    public void runNotes() {

        try {

            createRegistrationsFromCRM();

        } catch (IllegalStateException e){
            OpenLogItem.logError(new IllegalStateException(e));
            System.out.println("caught IllegalStateException in RunNotes()");
        }

    }

    private void createRegistrationsFromCRM(){

        System.out.println("in createRegistrationsFromCRM()!"); //TODO erase


        Database events = null;
        Database crm = null;
        Document person = null;
        Document registration = null;
        int i = 0;

        try{
            System.out.println("in try!"); //TODO erase
            if(session == null) System.out.println("session is null inside the createRegsFromCRM"); //TODO erase
            if(session != null) System.out.println("session is not null inside the createRegsFromCRM");//TODO erase
            try{
                events = session.getCurrentDatabase();
            } catch(Throwable e){
                System.out.println("error getting events " + e.getMessage() + " " + e.getCause());
                e.printStackTrace();
            }
            if(events == null) System.out.println("events is null inside the createRegsFromCRM");//TODO erase
            if(events != null) System.out.println("events is not null inside the createRegsFromCRM");//TODO erase
            System.out.println("tried to get events! events: " + events.toString()); //TODO erase
            if(events == null){
                System.out.println("getting hard coded database");
                events = session.getDatabase("MyServer/MyComp-dev/DE", "directory1\\directory2\\Datenbank_xp.nsf");
            }
            if(events == null) System.out.println("events2 is null inside the createRegsFromCRM");//TODO erase
            if(events != null) System.out.println("events2 is not null inside the createRegsFromCRM");//TODO erase 
            registration = events.createDocument();
            registration.replaceItemValue("Form", "frm_testDoc");
            registration.replaceItemValue("testField", "this is a test");
            registration.save();
        } catch (NotesException e) {
            System.out.println(e.getMessage());
        } finally {
            if(person != null) try{person.recycle();} catch (NotesException e) {}
            if(registration != null) try{registration.recycle();} catch (NotesException e) {}
            if(crm != null) try{crm.recycle();} catch (NotesException e) {}
            if(events != null) try{events.recycle();} catch (NotesException e) {}
        }
    }
}

是的,我从sessionScope bean调用它,这是当前的服务器日志:

02.12.2013 09:52:01   HTTP JVM: in cloner()
02.12.2013 09:52:01   HTTP JVM: in Reistrator.const
02.12.2013 09:52:01   HTTP JVM: in run!
02.12.2013 09:52:01   HTTP JVM: about to ask if notesSession is null
02.12.2013 09:52:01   HTTP JVM: notesSession is null!
02.12.2013 09:52:01   HTTP JVM: about to start runNotes()
02.12.2013 09:52:01   HTTP JVM: in createRegistrationsFromCRM()!
02.12.2013 09:52:01   HTTP JVM: in try!
02.12.2013 09:52:01   HTTP JVM: session is not null inside the createRegsFromCRM
02.12.2013 09:52:01   HTTP JVM: events is null inside the createRegsFromCRM
02.12.2013 09:52:01   HTTP JVM: in the run stack trace!
02.12.2013 09:52:01   HTTP JVM: null
02.12.2013 09:52:01   HTTP JVM: Exception in thread "pool-21-thread-1" 
02.12.2013 09:52:01   HTTP JVM: java.lang.IllegalStateException: NotesContext not initialized for the thread
02.12.2013 09:52:01   HTTP JVM:     at com.ibm.domino.xsp.module.nsf.NotesContext.getCurrent(NotesContext.java:123)
02.12.2013 09:52:01   HTTP JVM:     at com.ibm.domino.xsp.module.nsf.ModuleClassLoader$DynamicClassLoader.loadClass(ModuleClassLoader.java:383)
02.12.2013 09:52:01   HTTP JVM:     at java.lang.ClassLoader.loadClass(ClassLoader.java:638)
02.12.2013 09:52:01   HTTP JVM:     at de.holistic.utils.multithreading.AbstractBackgroundTask.run(AbstractBackgroundTask.java:65)
02.12.2013 09:52:01   HTTP JVM:     at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:906)
02.12.2013 09:52:01   HTTP JVM:     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:929)
02.12.2013 09:52:01   HTTP JVM:     at java.lang.Thread.run(Thread.java:738)

感谢您一看。

2 个答案:

答案 0 :(得分:1)

当你线程化NotesTask时,据我所知,你松开了“当前数据库”,“当前代理”的Session上下文 - 因为线程可以从任何地方运行。因此,访问session.currentDatabase()或session.currentAgent()很可能会从C核中冒出错误。

对我有用:永远不要让任何Notes对象跨越线程边界。仅使用本机Java对象(字符串,标准对象的集合,您自己的类等)。如果需要在线程之间传递数据库,我使用URL将其交给:

在主线程中:

  String dbURL = session.getNotesUrl();
  workerclass.setDbUrl(dbURL);

在工作线程中:

  events = (Database) session.resolve(this.dbUrl);

另外:让您的回收更容易。将此静态函数放入实用程序类:

/**
 * Get rid of all Notes objects
 * 
 * @param morituri
 *            = the one designated to die, read your Caesar!
 */
public static void shred(final Base... morituri) {
    for (Base obsoleteObject : morituri) {
        if (obsoleteObject != null) {
            try {
                obsoleteObject.recycle();
            } catch (NotesException e) {
                // We don't care we want go get
                // rid of it anyway
            } finally {
                obsoleteObject = null;
            }
        }
    }
}

然后替换:

        if(person != null) try{person.recycle();} catch (NotesException e) {}
        if(registration != null) try{registration.recycle();} catch (NotesException e) {}
        if(crm != null) try{crm.recycle();} catch (NotesException e) {}
        if(events != null) try{events.recycle();} catch (NotesException e) {}

使用:

        Utils.shred(person, registration, crm, events);

希望有所帮助。需要对currentDatabase采取一点谨慎措施。我发现即使克隆会话回收nsf可能会在你的主线程中爆炸。祝你好运!

答案 1 :(得分:1)

另一个想法是将代码包装到额外的XPage中并使用dojo.xhrGet()dojo.xhrPost()使用不同的参数多次启动XPage(由dojo.xhrGet / dojo.xhrPost调用给出) 。 这样,worker XPages可以给出返回值以指示操作是否成功,并且您可以在调用XPage上显示该信息。此外,您使用自定义多线程节省了很多头痛。