我最近遇到了很多关于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
事件中运行。
似乎我的问题已经解决,但我想了解这里发生了什么。
UPDATE1
如果我等待一段时间(可能超过10分钟)然后重新加载XPage,我仍然会在主数据库中收到错误。在另一个db我从来没有得到错误。
UPDATE2
昨天我从数据库中添加了完整的 xsp.properties ,它一直在工作。现在8小时后它仍然可以在我的原始数据库中正常工作。看起来我也需要这个:
xsp.persistence.mode=basic
表示“将页面保留在内存中”。似乎XPage在没有此设置的情况下立即(在单个HTTP请求中)被序列化。
答案 0 :(得分:8)
快速回答:永远不要永远,任何原因,将一个句柄存储到Domino对象的时间超过单个HTTP请求。 :)
我怀疑,因为Domino会话本质上是一个单例,将它存储在应用程序范围内会阻止它在每个请求结束时被回收,就像通常那样。在正常情况下,任何HTTP请求结束时XPage引擎都知道的任何Domino对象都会自动回收。因此,为了防止您收到的错误,最好不要将句柄存储在高于请求的任何范围内。
好消息是,至少对于当前Session
和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();
该类有一堆有用的可变分辨率方法,您可能想要查看。但是,使用自己的方便方法来解析变量对于任何上下文检查都很有用 - 例如,检索视图面板,数据表或重复的当前行;查询字符串参数map(param
);任何数据源。就像SSJS一样,您的Java代码通常在给定组件的上下文中触发(例如,按钮的onclick
事件处理程序),因此任何对该组件有效的变量都可以通过这种方式解析。 / p>
关于Domino对象存储的最后一点注意事项:只是存储元数据。因此,如果您要存储数据库(当前除此之外),请存储其文件路径或副本ID,并在需要访问它时,使用存储的元数据向当前会话询问数据库的句柄。同样,存储视图的名称,但不存储View
本身;存储文档的NoteID或UNID,但不存储实际的Document
。如果你发现你必须反复获取这些句柄,并因此希望它们被缓存,请重新审视逻辑结构的方式......代码应该被重构以建立一个句柄,做任何需要做/用的事情该对象,然后丢弃它(如果它不是会话,数据库或您也有数据源绑定的对象,请手动回收它)。
请记住任何实现Serializable
的 Java对象,并且不存储指向任何Domino对象的指针,只要您愿意,就可以存储。因此,为您的数据创建“模型”对象(并通过读取相应的Domino句柄来填充其属性),将这些对象存储在作用域中,然后在适当的时候写回相应的Domino对象。如果IBM没有尝试通过为我们创建文档和查看数据源来为我们简化XPage,那么这就是我们已经在做的事情(例如,我们将所有可编辑字段绑定到bean类的属性)例如Contact
,ExpenseReport
,Facility
等,而不是直接注意项目)。但是因为这些数据源做存在,我们仍然在思考“文档”和“视图”,而不是考虑数据所代表的人员,物理对象和业务流程。因此,我们在用户界面和后端数据之间保持不必要的链接,而不是仅在我们需要进行初始查询和最终更新(或创建)时触摸后端数据。很抱歉让我们变得如此富有哲理,但是当我们过度思考文档时,我们在XPage中尝试做的事情变得如此简单。 :)
答案 1 :(得分:1)
我认为这可能是由8.5.2的known错误引起的。我们遇到了同样的问题,只有解决方法是删除不必要的回收代码。 APAR中的示例不是很有用,它仅涵盖循环避免回收单个文档,但其他对象(数据库,视图)需要不同的方法。
编辑:可能存在与内部对象缓存相关的其他问题。不确定它是否已修复:当您要求某个对象时,您可能会从内部缓存中获取一个,这已经被回收(有意)。这可以通过打印Java对象ID来证明。这是8.5.1和8.5.2中的重大问题,我怀疑它是否发生在R9中。没试过,你。
答案 2 :(得分:0)
事实证明,当我对父类的父进行一些小改动并构建应用程序时,它开始工作。为了确保我复制粘贴原始代码,它仍然有用。
看起来这是某种构建问题。这不是我遇到这个问题的唯一情况,但下次我会尝试一些重新保存,清理和重建。