This chapter discusses the following topics:
How Sessions Keep Threads Organized
Associating Threads with Sessions
Which Threads Can Access Which Persistent Objects?
Description of ObjectStore Properties
Description of Concurrency Rules
How Sessions Keep Threads Organized
For a thread to use ObjectStore, it must be associated with a session. To use threads with ObjectStore, you must create at least one session and you must understand how to work with sessions.
Your application must create a session before it can use any of the ObjectStore API. After a session is created, it is an active session. A session remains active until your application or ObjectStore terminates it. After a session is terminated, it is never used again. You can, however, create a new session.
Concurrent sessions
In a single Java VM,
How Are Threads Related to Sessions?
At any given time, an active session has zero or more associated threads. Any number of threads can join a session. Each thread can belong to only one session at a time. Current thread and current session
The current thread is the thread that you are making a call from. The current session is the session the current thread belongs to. What Is the Benefit of a Session?
The benefit of a session is apparent when you want to have more than one session. Two sessions in the same Java process allow you to perform two distinct activities that involve ObjectStore. Each session has a clean, isolated view of the database. If you want to have two or more independent transactions going on at the same time, you can use two or more sessions. Concurrent sessions can be accessing the same database or different databases.
Independent threads
A need for many different independent transactions normally arises because you have many Java threads with different things going on in each one. Typically, this happens with a multithreaded application server, in which there are many threads. Each thread serves a different client, so you might want to have many threads. Each thread runs a separate transaction. Each thread is separate from each other thread. Cooperating threads
On the other hand, there are times when you have multiple threads that are cooperating on some database task, and must operate on the same objects at the same time. In this case, you might want two different Java threads to participate in the same transaction. Controlling which threads cooperate
Sessions allow you to control which threads cooperate in a transaction and which threads work in independent transactions. A session groups together a set of cooperating threads. Each session has a sequence (in time) of transactions, and a set of associated threads that participate in these transactions. Example of cooperating threads
A common case of cooperating threads arises when you are writing a Java applet. In an applet, there are calls to different parts of your program in different threads. You have to specify for ObjectStore that all these threads are part of the same session. This allows them to operate on the same objects and in the same transactions. A similar situation exists when you use RMI and CORBA servers. That is, there is a control mechanism that calls your methods in different threads. What Kinds of Sessions Are There?
An active session can be a global session or a nonglobal session. ObjectStore provides two kinds of sessions because, when you only need one session, there are many things ObjectStore can do for you automatically. Joining threads to sessions
As mentioned earlier, before you can use ObjectStore, you must create a session. For a thread to use ObjectStore, it must join a session. In a global session, an unassociated thread that makes a call to the ObjectStore API automatically joins the session. In a nonglobal session, this happens only when the call implies the session. See Rules for Automatically Joining a Thread to a Session. Otherwise, you must explicitly add the thread to a session.
Number of sessions
When there is an active global session, it is the only session in the Java VM. With PSE Pro and a future release of ObjectStore, you can have multiple nonglobal sessions or one global session in a Java VM. In the current release of ObjectStore and with PSE, there can be one session in a Java VM. It can be global or nonglobal. Global sessions
Global sessions make programming easier, because you do not need to know the ObjectStore APIs for associating threads with sessions. All threads that make ObjectStore API calls automatically join the one global session.
Creating Sessions
When you create a session, you initialize ObjectStore for use by the threads that become associated with that session. There are three ways to create a session:
Creating Global Sessions
When the session is a global one and a thread that is not associated with the session calls an ObjectStore API, ObjectStore automatically joins the thread to the session. After you create a global session, you do not need to be concerned about joining threads to the session.
public static Session createGlobal(String host, java.util.Properties properties)This method creates and returns a new session and designates the session as a global session. There are no threads joined to this session yet. Any thread, including the thread that creates the session, automatically joins the session the first time the thread uses ObjectStore.
ObjectStore ignores the first parameter; you can specify null. The second parameter specifies null or a property list. See Description of ObjectStore Properties.
If you try to create a global session when there is already an active session, ObjectStore throws ObjectStoreException.
public static Session getGlobal()If the global session is active, ObjectStore returns it. Otherwise, ObjectStore returns null.
Method signature
The method signature for creating a nonglobal session is
public static Session create(String host, java.util.Properties properties)This method creates and returns a new session. ObjectStore ignores the host argument; specify null. The second argument specifies null or a property list. See Description of ObjectStore Properties.
ObjectStore does not join the calling thread to the session.
Session name
ObjectStore generates a name for the session and never reuses that name for the lifetime of the process in which the session was created. If you want to specify a particular name, use the following overloading to specify a unique session name:
public static Session create(String host, java.util.Properties properties, String name)ObjectStore uses the session name in debugging messages. The Session.getName() method returns the name of the session.
Exception conditions
If you call Session.create() when there is an active global session, ObjectStore throws ObjectStoreException. If you are using ObjectStore, and you call Session.create() when there is an active nonglobal session, ObjectStore throws FatalApplicationException. Creating a Nonglobal Session with ObjectStore.initialize()
In previous releases of ObjectStore, the only way to create a session was to call the ObjectStore.initialize(host, properties) method. In this release, this method is provided for compatibility with earlier releases. This method might be deprecated in a future release. The method signature is
public static boolean initialize(String host, java.util.Properties properties)This method creates a new session, joins the calling thread to the session, and returns true. The host argument can be null. The second parameter specifies null or a property list. See Description of ObjectStore Properties.
Comparison with Session.create()
When you use the Session.create() method in place of the ObjectStore.initialize(host, properties) method, you must also call the Session.join() method. While the ObjectStore.initialize(host, properties) method starts a session and joins the calling thread to the session, the Session.create() method only starts the session. It does not join the calling thread to the session. False return value
When the three conditions listed below all exist, the ObjectStore.initialize() method returns false to indicate that it did not start a new session.
If there is already a global session, ObjectStore throws ObjectStoreException.
If there is already an active session, ObjectStore throws FatalApplicationException.
When a session is created, there is no associated transaction. While a session is active, an application can start and then commit or abort one transaction at a time per session. Over time, a session is associated with a sequence of transactions.
If there is a transaction in progress when an application or ObjectStore shuts down the session, ObjectStore aborts the transaction as part of the shutdown process.
Within a session, multiple databases can be open at the same time.
All transactions can access the same database. All sessions must have read-only transactions against that database.
See also Description of Concurrency Rules.
Transaction in progress?
To determine whether or not there is a transaction in progress, call the Session.inTransaction() method. The method signature is
public boolean inTransaction()If there is a transaction associated with the session, this method returns true. If there is no transaction associated with the session, this method returns false. If the session has been terminated, ObjectStore throws ObjectStoreException.
public Transaction currentTransaction()If the session has been terminated, ObjectStore throws ObjectStoreException. If no transaction is associated with the session, ObjectStore throws NoTransactionInProgressException.
public Session getSession()
public void terminate()It does not matter whether the session is a global session or a nonglobal session. ObjectStore shuts down the session. If there are no other sessions, no thread can use the ObjectStore API until there is a new active session. The terminated session is never reused. If you are using ObjectStore, you must start a new session before you can use the ObjectStore API again.
If ObjectStore throws FatalException, this shuts down the session.
Obtaining a Session
You can obtain a session with a call to any of the methods listed below:
public boolean isActive()If the session is active, this method returns true. If the session has been terminated, this method returns false.
Automatically Joining Threads to a Session
Whether a thread can automatically join a session depends on
After ObjectStore automatically joins a thread to a session,
As a result of explicit and implicit association, a session provides a context for a set of persistent objects, and a set of ObjectStore API objects, such a Database objects and a Transaction object.
The session defines a namespace. The namespace defines unique names (and consequently identities) for databases, segments, transactions, and persistent objects. While it is possible for threads in different sessions to share objects, doing so is incorrect and usually results in exceptions.
If the thread in which an object was materialized leaves the session, the object remains associated with the session.
public void join()This associates the current thread (the thread that contains the call to join()) with the session on which the join() method is called.
If the session has been terminated, or if the thread making the call is already associated with that session or some other session, ObjectStore throws ObjectStoreException.
To join a thread to a session for a bounded duration of time, try something like this:
Session session; session.create(null, myproperties); try { session.join(); ...; ...; ...; } finally { session.leave(); }
public static boolean initialize(Thread targetThread)If the specified target thread is already joined to a session, this method joins the current thread to that session and returns true. If the current thread is already joined to the target thread's session, this method returns false.
If the target thread is null, ObjectStore throws IllegalArgumentException. If the target thread is not associated with a session, ObjectStore throws ObjectStoreException, whether or not the session that the target thread was previously associated with is still active.
It does not matter whether or not the target thread has a transaction in progress. You can call ObjectStore.initialize(targetThread) at any time.
For ObjectStore, it is as if these operations are all coming from the same thread. It does not matter which operation comes from A and which operation comes from B. ObjectStore views the operations as being in a single sequence, because they are issued from cooperating threads.
If A or B starts a transaction, it does not matter which thread issues the call. The transaction begins for both threads regardless of which thread actually starts the transaction. Any changes performed by A or B during the transaction are visible to both threads and can be acted on by either thread. Similarly, if A commits the transaction, it is just as if B commits the transaction. So B must be in a state where it is okay to commit the transaction. A and B must cooperate.
Two or more noncooperating threads can open the same database at the same time and access the same root object. If two or more noncooperating threads access the same object in the database, there are an equivalent number of distinct instances of the persistent object - one for each thread. The identity test, ==, does not show them to be identical.
Noncooperating threads can experience deadlock.
Synchronizing Threads
Your application is responsible for synchronizing activity among cooperating threads when the transaction is committed or aborted. In general, your application must avoid accessing the database while a thread is committing the transaction and until a cooperating thread starts a new transaction. If a transaction is aborted, cooperating threads might need to retry database operations.
Removing Threads from Sessions
A thread can leave a session at any time, including while a transaction is in progress. This does not affect the transaction, nor any threads that are still joined to that session. With or without a transaction in progress, it is okay if there are no threads associated with a session. The session does not terminate. A thread can join a session later to finish the transaction. If no thread ever does that, ObjectStore aborts the transaction when the session terminates.
public static void leave()After you execute this method, the current thread is no longer joined to the session. If the current thread is already not associated with the session on which the method is called, ObjectStore throws ObjectStoreException.
If your application or ObjectStore shuts down a session, ObjectStore causes any associated threads to leave the session before it performs the shut down.
You can also call ObjectStore.shutdown() to remove a thread from a session. However, this method also shuts down the session when it is called on the last thread in the session.
If a thread is associated with a session and the thread terminates, it automatically leaves the session.
When your application calls the Session.createGlobal() or Session.create() method, ObjectStore does not associate the thread that calls the method with the newly created session. For that thread to join the new session, it must call the Session.join() method.
If a session has a transaction in progress, a thread that is not associated with that session must not use persistent objects that belong to that session. See Which Threads Can Access Which Persistent Objects?.
If a session does not have a transaction in progress, any thread, including threads that do not belong to that session, can access persistent objects to the degree they were left visible when the application committed or aborted the transaction. See Ending a Transaction.
Determining If ObjectStore Is Initialized for the Current Thread
You can use the Session.getCurrent() method to determine whether or not ObjectStore is initialized for the current thread. The method signature is Which Threads Can Access Which Persistent Objects?
Each persistent object is associated with exactly one session. Any modification to the state of a persistent object must be done by a thread that cooperates in the session to which the persistent object belongs.
At this point, in threadB you can read or modify objectA as long as there is no transaction in progress in sessionB. However, any modifications will be discarded when sessionA starts a transaction.
If you have a Java static variable that contains a persistent object and there are two separate sessions, you must decide which session owns the static variable. In other words, if there is a Java static variable whose value is a persistent object, that persistent object is associated with one session.
Array objects
When a thread commits a transaction, if ObjectStore reaches an object whose class does not implement IPersistent, ObjectStore treats the object as a transient object and migrates it to a database. This works correctly for immutable classes such as Integer and String. For array objects, this can cause unpredictable results, because one session might modify the object while another session is using the old contents. API Objects and Sessions
Each ObjectStore API object is related to one session. These metaobjects are
If you try to use a database in the wrong session, ObjectStore throws ObjectStoreException.
If you use Multiversion Concurrency Control (MVCC), there can be one writer and multiple readers.
Granularity of Concurrency
ObjectStore locks data at the page level. ObjectStore acquires a read lock on the object the first time the Java application reads or writes the contents of the object in a transaction. When that happens, the underlying C++ ObjectStore acquires a read lock on all pages used to store the object, as well as additional locks on other internal data structures (the info segment and schema segment). There is also Java-specific metadata that gets locked. Converting Read Locks to Write Locks
When a Java application modifies an object for which it already has a read lock, ObjectStore does not necessarily convert the read lock to a write lock immediately. The ObjectStore setLazyWriteLocking() method controls this behavior. If lazy write locking is true (the default) then ObjectStore only acquires write locks when it attempts to write the modified contents of the object to the database. That occurs either at commit time or when and if the application calls ObjectStore.evict() on the modified object.
Description of ObjectStore Properties
When you create a session, you can specify a properties argument. This section provides the following information about this argument:
There is only one system property list for each Java VM. If there are multiple sessions in the same Java VM, they all use the same system property list. For more information about system properties, see System.getProperty, in section 20.17.9 of the Java Language Specification.
Properties props = System.getProperties(); props.put("COM.odi.useDatabaseLocking", "true"); Session session = Session.create(null,props);There is also a System.setProperties() method that resets the System property list.
The JDK allows you to specify a system property by including
-D parameter= value
on the java command line before the class name. Each such specification defines one system property. Not all Java virtual machines run this way.
Properties props = new Properties(); props.put("COM.odi.useDatabaseLocking", "true"); Session session = Session.create(null,props);
For information about how to determine the optimum cache size, see the book ObjectStore Management, Chapter 3, OS_CACHE_SIZE environment variable.
Description of COM.odi.disableWeakReferences
The COM.odi.disableWeakReferences property defaults to "false". This means that ObjectStore uses the weak reference facility of the JDK. If you set this property to "true", it disables the weak reference facility and ObjectStore does not use it.
Description of COM.odi.migrateUnexportedStringsCOM.odi.migrateUnexportedStrings
The COM.odi.migrateUnexportedStrings property controls what happens when ObjectStore encounters a cross-segment reference to an unexported String object. If this property is not set or if it is set to true, ObjectStore creates a new String object that has the same value as the referenced object. ObjectStore places this new string in the same segment as the referring object and substitutes this new string for the referenced string. If this property is set to false, ObjectStore throws ObjectNotExportedException if it encounters a reference to a string in another segment and that string is not exported. Description of COM.odi.ObjectStoreLibraryCOM.odi.ObjectStoreLibrary
COM.odi.ObjectStoreLibrary specifies the name of the native C++ library that contains the ObjectStore schema and native methods for the application.
Description of COM.odi.password and COM.odi.user
COM.odi.user and COM.odi.password allow you to supply a user name and a password when the ObjectStore Server has Name Password set for the Authentication Required Server parameter. Description of COM.odi.product
COM.odi.product allows you to run multiple simultaneous sessions against different Object Design Java products in the same Java VM. Each Object Design Java product runs in its own session.
With the COM.odi.product property, a single process can run all of the following at the same time:
There are many ways this feature can be useful. For example, an application can:
1 Open the source database.
2 Read the objects you want to copy.
The ObjectStore.deepFetch() method is useful for doing this.
3 Commit the transaction with ObjectStore.RETAIN_READONY.
If you do not close the database, the persistent objects remain associated with the session in which you read them. This prevents another session from storing them in another database.
Here is an example of code that performs these steps.
import COM.odi.*; import COM.odi.util.*; import java.util.Properties; class CopyToOSJI implements ObjectStoreConstants { public static void main(String[] args) { /* Create some data in a PSE database and then read it out. */ Properties properties = new Properties(); properties.put("COM.odi.product", "PSE"); Session.create(null, properties).join(); Database database = Database.create( "pse.odb", ALL_READ | ALL_WRITE); Transaction.begin(UPDATE); OSVector vector = new OSVector(); vector.addElement(new Integer(3)); vector.addElement(new Integer(4)); database.createRoot("vector", vector); Transaction.current().commit(); Transaction.begin(READONLY); vector = (OSVector)database.getRoot("vector"); ObjectStore.deepFetch(vector); Transaction.current().commit(RETAIN_READONLY); /* Close the database and specify true to retain persistent objects as transient objects. */ database.close(true); Session.leave(); /* Copy the data to an ObjectStore database. */ properties.put("COM.odi.product", "ObjectStore"); Session.create(null, properties).join(); database = Database.create("osji.odb", ALL_READ | ALL_WRITE); Transaction.begin(UPDATE); database.createRoot("vector", vector); Transaction.current().commit(); database.close(); } }
When ObjectStore is about to migrate a string to the database, it first checks the string pool for an identical string. If it finds one, it uses the string that is already stored in the database instead of adding a new identical string to the database.
The information about which strings are available to be shared is maintained only for the current transaction. The strings that are available to be shared are maintained in a string pool. ObjectStore resets the string pool to empty at the start of each transaction.
For example, suppose you create two instances of a Person object in a transaction. In each instance, the value of the name field is Lee. If you store both instances in the database in the same transaction, ObjectStore adds only one instance of the string "Lee" to the database. This is true even though the Java VM might contain two instances of the string "Lee". When ObjectStore writes the first "Lee" string in the database, it notes it in the string pool. Before ObjectStore stores the next instance of "Lee" in the database, it checks the string pool to see if an identical instance is already in the database.
Continuing the example, suppose you use two transactions and you store one instance of Person in each transaction. The result is that there are two identical "Lee" strings in the database. This is because ObjectStore resets the string pool to be empty at the start of each transaction. Consequently, ObjectStore cannot reuse the "Lee" string from the previous transaction.
When ObjectStore encounters an object of a type for which it does not have information (that is, the type is unregistered), it checks the setting of the COM.odi.trapUnregisteredType property.
If the property is not set, ObjectStore creates an instance of the UnregisteredType class to represent the object of the unknown type. Your application continues to run as long as it does not try to use the UnregisteredType object. Often, this can be fine because your application has no need for that particular field. However, if you do try to use the object of the unregistered type, ObjectStore throws ClassCastException.
If COM.odi.trapUnregisteredType is set, ObjectStore does not create an UnregisteredType object. Instead, it throws FatalApplicationException and provides a message that indicates the name of the unregistered class. For additional information, see Handling Unregistered Types.
Updated: 10/07/98 08:44:58