Xpages xsp.application.timeout和;物品已被移除或回收;

Xpages xsp.application.timeout和;物品已被移除或回收;,xpages,xpages-ssjs,Xpages,Xpages Ssjs,最近我遇到了很多关于Java类级Domino对象消失的问题。例如,我将一个lotus.domino.Session放在一个(非静态)类级别变量中,当我尝试在下一个代码行中使用它时,我得到: NotesException: Object has been removed or recycled 在开始使用托管bean之前,我没有遇到这些问题,但现在我似乎一直在使用请求范围的bean以及普通Java对象来解决这些问题。我一直在向许多地方添加isRecycled()检查,一直在想为什么以前不必这样做

最近我遇到了很多关于Java类级Domino对象消失的问题。例如,我将一个
lotus.domino.Session
放在一个(非静态)类级别变量中,当我尝试在下一个代码行中使用它时,我得到:

NotesException: Object has been removed or recycled
在开始使用托管bean之前,我没有遇到这些问题,但现在我似乎一直在使用请求范围的bean以及普通Java对象来解决这些问题。我一直在向许多地方添加
isRecycled()
检查,一直在想为什么以前不必这样做。我知道Domino对象不是序列化的,而是在它们在请求或代理期间停留之前

今天,我将给出此异常的代码复制到另一个db,但在那里没有发生异常。然后,我将xsp.properties从该数据库复制到原始数据库,那里也没有发生异常。通过一次删除一行,我发现如果我有:

xsp.application.timeout=10
我没有得到异常,如果我删除它,我会得到异常。有人知道为什么吗?默认值应为30分钟,但除非设置应用程序超时,否则我的会话对象似乎会在纳秒内消失。我将会话从SSJS传递到Java,并将其存储在构造函数代码中:

private Session session;    

public Domino(Session session) {
    this.session = session;
}
正如您所看到的,这不是一个托管bean。我测试的Domino版本是9.0.1,但我也需要在8.5.2中使用这段代码。代码正在
beforePageLoad
事件中运行

看来我的问题已经解决了,但我想知道这里发生了什么

更新1

如果我等待一段时间(可能超过10分钟),然后重新加载XPage,我仍然会在主数据库中得到错误。在另一个数据库中,我从未得到错误

更新2

昨天,我从一直工作的数据库中添加了完整的xsp.properties。现在8小时后,它仍然可以在我原来的数据库中正常工作。看起来我还需要这个:

xsp.persistence.mode=basic

这意味着“将页面保留在内存中”。似乎XPage在没有此设置的情况下会立即序列化(在单个HTTP请求中)。

快速回答:无论出于何种原因,永远不要将Domino对象的句柄存储在单个HTTP请求之外的任何时间。:)

我怀疑,因为Domino会话本质上是一个单例会话,所以将其存储在应用程序范围中会阻止它在每个请求结束时被回收,这与通常情况下是一样的。在正常情况下,XPage引擎在任何HTTP请求结束时意识到的任何Domino对象都会被自动回收。因此,为了防止您收到的错误,最好的做法是不在高于请求的任何范围内存储句柄

好消息是,至少对于当前的
会话
数据库
,您不需要这样做:您可以向变量解析器请求它

SSJS具有对变量解析器的内在访问权限,因为所有SSJS代码都必须在运行时进行评估;因此,任何对会话的引用,例如,都必须询问变量解析器“会话”当前的计算结果是什么。当计算整个表达式时,这会自动发生,但我们可以从自己的Java代码手动访问变量解析器:

FacesContext context = FacesContext.getCurrentInstance();
Application app = context.getApplication();
VariableResolver resolver = app.getVariableResolver();
Session currentSession = (Session) resolver.resolveVariable("session", context);
Session currentSession = (Session) VariableUtils.resolveVariable("session");
Database currentDatabase = (Database) VariableUtils.resolveVariable("database");
DominoDocument currentDocument = (DominoDocument) VariableUtils.resolveVariable("currentDocument");
每次我们想要获取变量句柄时,键入这些都是毫无意义的,解析上下文变量通常很有用(因为这适用于在SSJS中也有效的任何变量,而不仅仅是Domino对象),因此我建议将其包装在静态实用工具方法中:

public class VariableUtils {

 public static Object getVariableValue(String variable) {
  FacesContext context = FacesContext.getCurrentInstance();
  Application app = context.getApplication();
  VariableResolver resolver = app.getVariableResolver();
  return resolver.resolveVariable(variable, context);
 }

}
然后,您可以轻松解析任何Java代码中的任何变量:

FacesContext context = FacesContext.getCurrentInstance();
Application app = context.getApplication();
VariableResolver resolver = app.getVariableResolver();
Session currentSession = (Session) resolver.resolveVariable("session", context);
Session currentSession = (Session) VariableUtils.resolveVariable("session");
Database currentDatabase = (Database) VariableUtils.resolveVariable("database");
DominoDocument currentDocument = (DominoDocument) VariableUtils.resolveVariable("currentDocument");
如果你的应用加载了扩展库,那么对于前两个,已经有一个静态方法可用:

Session currentSession = (Session) ExtLibUtils.getCurrentSession();
Database currentDatabase = (Database) ExtLibUtils.getCurrentDatabase();
该类有一系列有用的变量解析方法,您可能想查看这些方法。但是,拥有自己方便的解析变量的方法对于任何上下文检查都是有用的——例如,检索视图面板、数据表或repeat的当前行;查询字符串参数映射(
param
);任何数据源。与SSJS一样,Java代码通常在给定组件的上下文中触发(例如,按钮的
onclick
事件处理程序),因此任何对该组件有效的变量都可以通过这种方式解析

关于Domino对象存储的最后一个注意事项:只需存储元数据即可。因此,如果要以其他方式存储数据库(而不是当前数据库),请存储其文件路径或副本ID,并且在需要访问它时,使用存储的元数据向当前会话请求数据库句柄。同样,存储视图的名称,但不存储
视图本身;存储文档的NoteID或UNID,但不存储实际的
文档
。如果您发现必须重复获取这些句柄,因此希望它们被缓存,请重新查看逻辑的结构方式。。。应该重构代码以建立一个句柄,对该对象执行任何需要执行的操作,然后丢弃它(如果它不是会话、数据库或绑定了数据源的对象,则手动回收它)

请记住,任何实现
Serializable
且不存储指向任何Domino对象的指针的Java对象都可以存储任意长的时间。因此,为您的数据创建“模型”对象(并通过读取相应的Domino句柄来填充它们的属性),将这些对象存储在范围中,然后在适当的时候写回相应的Domino对象。如果IBM没有试图通过为我们创建文档和视图数据源来简化XPages,这就是我们已经在做的事情(例如,我们将把所有可编辑字段绑定到bean类的属性上,比如
Contact
ExpenseReport
Facility
,等等,而不是直接记录项)。但是因为这些数据源