|
||||||||||
| PREV CLASS NEXT CLASS | FRAMES NO FRAMES | |||||||||
| SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD | |||||||||
java.lang.Objectorg.dellroad.stuff.pobj.PersistentObject<T>
T - type of the root persistent objectpublic class PersistentObject<T>
Main class for Simple XML Persistence Objects (POBJ).
Instances model an in-memory "database" represented by a root Java object and the graph of other objects that it references. The object graph is backed by a persistent XML file, which is read at initialization time and re-written after each change.
Changes are applied "wholesale" to the entire object graph, and are serialized and atomic. In other words, the
entire object graph is read from, and written to, this class by value. As a result, it is not possible to change
only a portion of the "database". The entire object graph is read and written as one thing. Similarly, the
persistent XML file is updated by writing out a new, temporary copy and renaming the copy onto the original,
using File.renameTo() for atomicity (on systems that support it, e.g., UNIX variants).
When the object graph is updated, it must pass validation checks, and then the persistent XML file is updated and listener notifications are sent out. Support for delayed write-back of the persistent XML file is included: this allows modifications that occur in rapid succession to be consolidated into a single filesystem write operation.
Support for optimistic locking is included. There is a "current version" number which is incremented each time the object graph is updated; writes may optionally specify this number to ensure no intervening changes have occurred. If concurrent updates are expected, applications may choose to implement a 3-way merge algorithm of some kind to handle optimistic locking failures.
Instances can be configured to preserve one or more backup copies of the persistent file on systems that support
hard links (requires JNA; see HardLink).
Set the numBackups property to enable.
When a non-zero check interval is configured, instances support "out-of-band" writes
to the XML persistent file by some other process. This can be handy in cases where the other process (perhaps hand edits)
is updating the persistent file and you want to have a running process pick up the changes just as if
setRoot() had been invoked. In particular, instances will detect the appearance
of a new persistent file after an instance has started without one. In all cases, persistent objects must properly validate.
A special case of this is effected when setRoot() is never explicitly invoked
by the application. Then some other process must be responsible for all database updates, and this class automatically
picks them up, validates them, and send out notifications.
An "empty start" occurs when an instance is started but the persistent XML file is either missing,
does not validate, or cannot be read for some other reason. In such cases, the instance will start with no object graph,
and getRoot() will initially return null. This situation will correct itself as soon as the object graph is written
via setRoot() or the persistent file appears (effecting an "out-of-band" update).
Whther empty starts are allowed is determined by the allowEmptyStart property (default
false). When empty starts are disallowed, then start() will instead throw a PersistentObjectException.
In this configuration, getRoot() can be relied upon to always return a non-null, valid root.
Instances must be configured with a PersistentObjectDelegate that knows how to validate the object graph
and perform conversions to and from XML. See PersistentObjectDelegate and its implementations for details.
Like any database, the XML schema may evolve over time. The PersistentObjectSchemaUpdater class provides a simple
way to apply and manage schema updates using XSLT transforms.
PersistentObjectDelegate| Field Summary | |
|---|---|
protected Logger |
log
|
| Constructor Summary | |
|---|---|
PersistentObject(PersistentObjectDelegate<T> delegate,
File file)
Simplified constructor configuring for immediate write-back and no persistent file checks. |
|
PersistentObject(PersistentObjectDelegate<T> delegate,
File file,
long writeDelay,
long checkInterval)
Constructor. |
|
| Method Summary | |
|---|---|
void |
addListener(PersistentObjectListener<T> listener)
Add a listener to be notified each time the object graph changes. |
void |
checkFile()
Check the persistent file for an "out-of-band" update. |
long |
getCheckInterval()
Get the delay time between periodic checks for changes in the underlying persistent file. |
protected PersistentObjectDelegate |
getDelegate()
Get the configured PersistentObjectDelegate. |
int |
getNumBackups()
Get the number of backup copies to preserve. |
File |
getPersistentFile()
Get the persistent file containing the XML form of the persisted object. |
T |
getRoot()
Atomically read the root object. |
T |
getSharedRoot()
Get a shared copy of the root object. |
long |
getVersion()
Get the version of the current root. |
long |
getWriteDelay()
Get the maximum delay after an update operation before a write-back to the persistent file must be initiated. |
boolean |
isAllowEmptyStart()
Determine whether this instance should allow an "empty start". |
boolean |
isStarted()
Determine whether this instance is started. |
protected void |
notifyListeners(long newVersion,
T oldRoot,
T newRoot)
Notify listeners of a change in value. |
protected T |
read()
Read the persistent file. |
void |
removeListener(PersistentObjectListener<T> listener)
Remove a listener added via addListener(). |
void |
setAllowEmptyStart(boolean allowEmptyStart)
Configure whether an "empty start" is allowed. |
void |
setNumBackups(int numBackups)
Set the number of backup copies to preserve. |
void |
setRoot(T newRoot)
Atomically update the root object. |
void |
setRoot(T newRoot,
long expectedVersion)
Atomically update the root object. |
void |
start()
Start this instance. |
void |
stop()
Stop this instance. |
String |
toString()
Get a simple string description of this instance. |
protected void |
write(T obj)
Write the persistent file and rotate any backups. |
| Methods inherited from class java.lang.Object |
|---|
clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait |
| Field Detail |
|---|
protected final Logger log
| Constructor Detail |
|---|
public PersistentObject(PersistentObjectDelegate<T> delegate,
File file,
long writeDelay,
long checkInterval)
The writeDelay is the maximum delay after an update operation before a write-back to the persistent file
must be initiated.
delegate - delegate supplying required operationsfile - the file used to persistwriteDelay - write delay in milliseconds, or zero for immediate write-backcheckInterval - check interval in milliseconds, or zero to disable persistent file checks
IllegalArgumentException - if delegate or file is null
IllegalArgumentException - if writeDelay or checkInterval is negative
public PersistentObject(PersistentObjectDelegate<T> delegate,
File file)
Equivalent to:
PersistentObject(delegate, file, 0L, 0L);
| Method Detail |
|---|
public File getPersistentFile()
public long getWriteDelay()
public long getCheckInterval()
public long getVersion()
This returns a value which increases monotonically with each update.
The version number is not persisted with the persistent file; each instance of this class keeps
its own version count. The version is reset to zero when stop() is invoked.
public int getNumBackups()
Backup files have suffixes of the form .1, .2, etc.,
in reverse chronological order. Each time a new root object is written, the existing files are rotated.
Back-ups are created via hard links and are only supported on UNIX systems.
The default is zero, which disables backups.
public void setNumBackups(int numBackups)
IllegalArgumentException - if numBackups is negativegetNumBackups()public boolean isAllowEmptyStart()
The default for this property is false.
public void setAllowEmptyStart(boolean allowEmptyStart)
The default for this property is false.
public boolean isStarted()
public void start()
PersistentObjectException - if an error occurspublic void stop()
PersistentObjectException - if a delayed write back is pending and error occurs while performing the writepublic T getRoot()
If there is no persistent file and no value has been set, null will be returned. However the persistent file may appear "out of band" at any time; if so, this will be detected within the configured check interval.
This returns a deep copy of the current root object; any subsequent modifications are not written back.
IllegalStateException - if this instance is not started
PersistentObjectException - if an error occurspublic T getSharedRoot()
This returns a copy of the root object, but it returns the same copy each time until the next change.
This method is more efficient than getRoot(), but all callers must agree not to modify the returned object
or any object in its graph of references.
public final void setRoot(T newRoot,
long expectedVersion)
The given object is deep-copied and the copy replaces the current root.
If expectedVersion is non-zero, then if the current version is not equal to it,
a PersistentObjectVersionException exception is thrown. This mechanism
can be used for optimistic locking.
newRoot - new persistent objectexpectedVersion - expected current version number, or zero to ignore the current version number
IllegalArgumentException - if newRoot is null
IllegalArgumentException - if version is negative
IllegalStateException - if this instance is not started
PersistentObjectException - if an error occurs
PersistentObjectVersionException - if version is non-zero and not equal to the current version
PersistentObjectValidationException - if the new root has validation errorspublic final void setRoot(T newRoot)
The is a convenience method, equivalent to:
setRoot(newRoot, 0)
This method cannot throw PersistentObjectVersionException.
public void checkFile()
If the persistent file has a newer timestamp than the timestamp of the most recently read or written version, then it will be read and applied to this instance.
IllegalStateException - if this instance is not started
PersistentObjectException - if an error occurspublic void addListener(PersistentObjectListener<T> listener)
IllegalArgumentException - if listener is nullpublic void removeListener(PersistentObjectListener<T> listener)
addListener().
public String toString()
toString in class Objectprotected PersistentObjectDelegate getDelegate()
PersistentObjectDelegate.
protected T read()
PersistentObjectException - if an error occursprotected final void write(T obj)
A temporary file is created in the same directory and then renamed to provide for an atomic update (on supporting operating systems).
IllegalArgumentException - if obj is null
PersistentObjectException - if an error occurs
protected void notifyListeners(long newVersion,
T oldRoot,
T newRoot)
newVersion - the version number associated with the new root
|
||||||||||
| PREV CLASS NEXT CLASS | FRAMES NO FRAMES | |||||||||
| SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD | |||||||||