1

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

NotesException: Object has been removed or recycled

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

今天我将给出此异常的代码复制到另一个数据库,但没有发生异常。然后我将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 请求中)。

4

3 回答 3

8

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

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

好消息是,至少对于当前的Sessionand Database,您永远不需要:您可以向变量解析器询问它。

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

FacesContext context = FacesContext.getCurrentInstance();
Application app = context.getApplication();
VariableResolver resolver = app.getVariableResolver();
Session currentSession = (Session) resolver.resolveVariable("session", context);

每次我们想要获取变量的句柄时都要输入很多废话,并且解析上下文变量通常很有用(因为这适用于在 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 代码中解析任何变量:

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();

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

关于存储 Domino 对象的最后一点说明:只存储元数据。因此,如果您要存储数据库(不是当前的),请存储其文件路径或副本 ID,当您需要访问它时,使用存储的元数据向当前会话询问数据库的句柄。同样,存储视图的名称,但不存储视图View本身;存储文档的 NoteID 或 UNID,而不是实际的Document. 如果您发现您必须反复获取这些句柄,因此希望它们被缓存,请重新审视您的逻辑结构方式......代码应该被重构以建立一个句柄,做任何需要做的事情 / 到该对象,然后丢弃它(如果它不是会话、数据库或您还绑定了数据源的对象,请手动回收它)。

请记住,任何实现Serializable但不存储指向任何 Domino 对象的指针的 Java 对象都可以根据需要存储。因此,为您的数据创建“模型”对象(并通过读取相应的 Domino 句柄来填充它们的属性),将这些对象存储在范围内,然后在适当的时候写回相应的 Domino 对象。如果 IBM 没有尝试通过为我们创建文档和视图数据源来为我们简化 XPage,这就是我们已经在做的事情(例如,我们会将所有可编辑字段绑定到 bean 类的属性如Contact, ExpenseReport,Facility等,不直接标注项目)。但是因为这些数据源确实存在,我们仍然在考虑“文档”和“视图”,而不是考虑数据所代表的人、物理对象和业务流程。因此,我们在用户界面和后端数据之间维护了不必要的链接,而不是仅在需要进行初始查询和最终更新(或创建)时才接触后端数据。很抱歉变得如此哲学,但是当我们超越文档思考时,我们在 XPages 中尝试做的很多事情变得非常容易。:)

于 2013-11-05T23:06:02.583 回答
1

我认为这可能是由8.5.2的已知错误引起的。我们遇到了同样的问题,唯一的解决方法是在不需要的地方删除回收代码。APAR 中的示例不是很有帮助,它只涵盖了循环避免回收单个文档,但其他对象(数据库、视图)需要不同的方法。

编辑:可能存在与内部对象缓存相关的另一个问题。不确定它是否已修复:当您请求某个对象时,您可能会从内部缓存中获得一个,该缓存已被回收(有意)。这可以通过打印 Java 对象 ID 来证明。这是 8.5.1 和 8.5.2 中的大问题,我怀疑它发生在 R9 中。没测试,呵呵。

于 2013-11-06T11:33:15.190 回答
0

事实证明,当我对父类的父类进行小改动并构建应用程序时,它就开始工作了。为了确保我复制粘贴了原始代码并且它仍然有效。

看起来这是某种构建问题。这不是我看到这个问题的唯一情况,但下次我会尝试重新保存、清理和重建。

于 2013-11-07T17:03:30.253 回答