使用 IBM FileNet P8 实现序列号分发器
2009-12-09 00:00:00 来源:WEB开发网ObjectStore 中的每个独立的可持久化对象都是以一个 Update Sequence Number (USN) 进行标记。这不是常规意义上的属性,但它的值是通过 IndependentlyPersistableObject.getUpdateSequenceNumber() 方法公开的。当每次 ObjectStore 中的对象被更新时,CE 服务器将自动递增 USN。当您从服务器获取一个对象时,USN 也被获取并传递到客户端。API 将 USN 作为 save() 对象的一部分发送回到服务器。如果发送的 USN 值与储存库中储存的值不匹配,CE 服务器就知道已经(由其他调用方)进行了更新,因此该对象已被获取。这是数据库调用乐观(optimistic)锁的简单方式。
如果 CE 服务器检测到交叉更改,它将合理地进行利用,这与合作锁特性不同。通过 CE API,您可以使用称为无获取实例化(在其他地方也提到)绕过服务器检查,但在这个用例中您要为此付出更多的努力。从本地在 API 中实例化一个对象而没有从服务器获取它称为无获取实例化。在这些情况中,USN 值有一个表示 CE 服务器跳过 USN 检查的特殊值。这有时被称为未保护更新。如果您稍后从服务器获取或刷新任何属性,也会获取当前 USN。
不过,在我们的例子中执行无获取实例化和未保护更新没有任何意义。要获取计数器属性的当前值,必须从服务器获取它。可能有人会恶意地通过未保护更新破坏计数器属性,但这种破坏也可以发生在常规的更新中。因此,这里没有出现新的威胁。因为用例存在语义,所以通过编写错误代码搞破坏的机会是很低的。
要利用 USN 检查和 CE 服务器的第一次写优先策略,您需要尝试在分发器对象中更新计数器,然后检查报告交叉更改的错误。清单 3 显示了该用例的一个例子。
清单 3. 第一次写优先private static final String COUNTER_PROPERTY_NAME = "WjcCounter";
/**
* This property filter is used to minimize data returned in fetches and refreshes.
*/
private static final PropertyFilter PF_COUNTER = new PropertyFilter();
static
{
PF_COUNTER.addIncludeProperty(1, null, null, COUNTER_PROPERTY_NAME, null);
}
/**
* Get the next value efficiently by exploiting First Writer Wins
*/
public int getNextValue(boolean feelingUnlucky)
{
final Properties dispenserProperties = dispenser.getProperties();
// Object might be updated by someone else, so try a few times
for (int attemptNumber=0; attemptNumber<10; ++attemptNumber)
{
// If cached data invalid, fetch the current value
// from the server. This also covers the fetchless
// instantiation case.
if (feelingUnlucky
|| dispenser.getUpdateSequenceNumber() == null
|| !dispenserProperties.isPropertyPresent(COUNTER_PROPERTY_NAME))
{
// fetchProperties will fail if the USN doesn't match, so null it out
dispenser.setUpdateSequenceNumber(null);
dispenser.fetchProperties(PF_COUNTER); // R/T
}
int oldValue = dispenserProperties.getInteger32Value(COUNTER_PROPERTY_NAME);
int newValue = oldValue + 1;
dispenserProperties.putValue(COUNTER_PROPERTY_NAME, newValue);
try
{
// Because we use a refreshing save, the counter property's
// new value will be returned from the server.
dispenser.save(RefreshMode.REFRESH, PF_COUNTER); // R/T
return newValue;
}
catch (EngineRuntimeException ere)
{
ExceptionCode ec = ere.getExceptionCode();
if (ec != ExceptionCode.E_OBJECT_MODIFIED)
{
// If we get an exception for any reason other than
// the object being concurrently modified, rethrow it.
throw ere;
}
// Someone else modified it. Invalidate our cached data and try again.
dispenser.setUpdateSequenceNumber(null);
dispenserProperties.removeFromCache(COUNTER_PROPERTY_NAME);
continue;
}
}
// too many iterations without success
throw new RuntimeException("Oops");
}
/**
* Set by constructor or some other means.
* Fetchless instantiation is OK.
*/
private final CustomObject dispenser;
更多精彩
赞助商链接