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)
感谢您一看。
答案 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上显示该信息。此外,您使用自定义多线程节省了很多头痛。