Complete Contents
Introduction
Chapter 1 About Netscape Application Server Extensions
Chapter 2 About the Netscape Extension Builder
Chapter 3 Introduction to Netscape's Interface Definition Language
Chapter 4 Designing a Netscape Extension
Chapter 5 Generating Output Files
Chapter 6 Completing Method Stubs
Chapter 7 Using Template Streaming
Chapter 8 Managing State and Session Information
Chapter 9 Using Object Pools
Chapter 10 Compiling the Extension Source Code
Chapter 11 Deploying and Managing a Netscape Extension
Chapter 12 Example Extension: HelloWorld
Appendix A C++ Helper Functions
Appendix B Java Helper Static Methods
Appendix C Java Class Decorations
Appendix D Reserved Words
Appendix E The ConnManager.cpp File
Glossary
Previous Next Contents Index


Managing State and Session Information

This chapter describes extension state and session management, which maintains application state and user session information for extended technology.

The following topics are included in this chapter:


About State and Session Management
Application state and user session information is maintained by extension state and session management for the extended technology. You most likely want to enable this feature when your legacy solution tracks user information on a user-by-user basis, or the legacy solution is part of an application that is distributed across multiple servers.

Managing state and session information is supported by Netscape extensions as well as by applications, but the usage is slightly different for extensions. This section of Developer's Guide presents only those differences.

When enabling an extension to use state and session data, there are no decorations to add in Netscape Extension Builder Designer. You implement the code directly, after the source code is generated.

Extension state and session management is provided through the following Netscape Application Server interfaces:

Java
C++
As you are filling in the method stubs of an extension, you can use these interfaces to enable an extension to store the user session and/or application state information.

For information about managing user sessions and application state from applications, see


Using Application State in an Extension
Application state management in extensions works the same as it does in application components, except for the method you use to access the state interface. The state interface is IGXState2 for C++ extensions and IState2 for Java extensions.

State data is useful if, for example, you want to track the total number of users of your extension.

To use application state:

  1. Create the root node of the state tree.
  2. Set and manage the state data.
Creating the Root Node in a C++ Extension
To create the root node of the state tree, use the GXGetStateTreeRoot( ) helper function. The following code example shows how to create a state tree and a child node:

HRESULT hr;

hr = GXGetStateTreeRoot(m_pContext, GXSTATE_DISTRIB|GXSTATE_PERSISTENT,

"Grammy", &m_pStateRoot);

if (hr == NOERROR && m_pStateRoot)

{

IGXState2 *pState = NOERROR;

hr = m_pStateRoot->GetStateChild("Best Female Vocal",

&pState);

if (hr != NOERROR || !pState)

{

hr = m_pStateRoot->CreateStateChild("Best Female Vocal",

0, GXSTATE_DISTRIB|GXSTATE_PERSISTENT, &pState);

For more information on GXGetStateTreeRoot( ), see C++ Helper Functions.

Creating the Root Node in a Java Extension
To create the root node of the state tree, use the GetStateTreeRoot( ) helper static method in the GXContext class. The following code example shows how to create a state tree and a child node:

IState2 tree = GXContext.GetStateTreeRoot(m_Context,

GXSTATE.GXSTATE_DISTRIB|GXSTATE.GXSTATE_PERSISTENT,

"Grammy");

if (tree!=null)

{

IState2 child = tree.getStateChild("Best Female Vocal");

if (child == null)

{

child = tree.createStateChild("Best Female Vocal", 0,

GXSTATE.GXSTATE_DISTRIB|GXSTATE.GXSTATE_PERSISTENT);

For more information on GetStateTreeRoot( ), see Java Helper Static Methods.

Setting and Managing State Data
To set and manage the state data, use methods defined either in the IGXState2 interface for C++ extensions or in the IState2 interface for Java extensions. In C++ extensions, for example, use CreateStateChild( ) to create child nodes, and use SetStateContents( ) to place state information into the node. Similarly, for Java extensions use createStateChild( ) and setStateContents( ).

The string parameter in these methods cannot exceed 31 characters.


Using Sessions in an Extension
User sessions work the same in an extension as they do in an application component, except for the method you use to access the session interface. The session interface is IGXSession2 for C++ extensions and ISession2 in Java extensions.

Internally, session data for extensions and applications is stored in separate memory spaces; however, extension session data has the same session ID as that of its calling application. Therefore, in order for session management to work in an extension, the application must have previously created an application session.

To use sessions in an extension, use the thread session method as described in the following paragraphs. This method is implemented by the Manager class in each of your extension services.

C++
In C++ extensions, use the GetThreadSession( ) method, which has the following syntax:

HRESULT GetThreadSession(

DWORD sessionType,

IGXSession2 **ppSession);

sessionType is either GXSESSION_LOCAL or GXSESSION_DISTRIB, indicating that the session is either local or distributed. ppSession is a pointer to the created IGXSession2 object.

The following code example accesses a local session:

IGXSession2* pSession2;

mgr->GetThreadSession(GXSESSION_LOCAL, &pSession2)

Java
In Java extensions, use the getThreadSession( ) method, which has the following syntax:

public ISession2 getThreadSession(

int sessionType)

sessionType is either GXSESSION.GXSESSION_LOCAL or GXSESSION.GXSESSION_DISTRIB, indicating that the session is either local or distributed

The following code example accesses a distributed session:

ISession2 session2=

mgr.getThreadSession(GXSESSION.GXSESSION_DISTRIB)


Local Versus Distributed State and Session Data
State and session objects can be local or distributed. An extension can have local and distributed state/session objects at the same time.

Distributed data is replicated on multiple servers and can be accessed from any of those servers. By contrast, local data can be accessed from only one server.

Use distributed data whenever possible, for two main reasons. First, Java extensions do not support local state or session data, so you are limited to using C++ extensions.

Second, local state or session data requires the use of sticky load balancing. Applications must be "sticky" if they access extensions that rely on local state or session data. A sticky application is one that always returns to the same process for the same user. Sticky load balancing is described in more detail in Administration Guide.

Using a Distributed Session or State
Use a distributed session or state if the data to be put into the session or state can be completely described using any combination of the following methods:

C++
SetValString( ), SetValInt( ), SetValBlob( ) in the IGXValList interface.

Java
setValString( ), setValInt( ), setValBlob( ) in the IValList interface.

In other words, data can be distributed if you can represent it as either a string, an integer, or a blob.

For C++ extensions, session data is stored in an IGXValList object. To get this data, use:

IGXSession2.GetSessionData()

For Java extensions, session data is stored in an IValList object. To get this data, use:

ISession2.getSessionData()

Using a Local Session or State
Local state or session data is supported only for C++ extensions. Use a local session or state if the data does not meet the criteria for distributed session or state. For example, objects that rely on physical connections, such as database or result-set objects, cannot be placed into a distributed session or state.

To place data into a local session, derive the data object from the IGXObject interface. The IGXObject interface allows the data in a session to be destroyed at the same time that the session is destroyed. A session is destroyed whenever it times out.

If the data to be placed in the local session has implemented the IGXObject interface, place the data directly into an IGXValList.

However, if the data does not implement IGXObject, perform the following steps to place the data in a local session:

  1. Use the GX_LOCALSESSION_CLASS_DECL macro to define a helper class that will go in the session. For example:
  2. GX_LOCALSESSION_CLASS_DECL(MyClassName);

  3. Use the GX_LOCALSESSION_CLASS_INST macro to create an instance of the helper class, which will be used to wrap the instance of your user-defined class. For example:
  4. GXObject* pWrapObject=GX_LOCALSESSION_CLASS_INST (MyClassName, pMyHeapObjectInstance);

  5. Put pWrapObject in the session's IGXValList object as follows:
  6. GXVAL val;

    val.vt = GXVT_UNKNOWN;

    val.v.punkVal=pWrapObject;

    pValList->SetVal("MySessionData",&val);

    pSession->SaveSession(0);

  7. Use the GX_LOCALSESSION_CLASS_UNWRAP macro to retrieve your original object. For example:
  8. IGXValList *pSessVlist=session->GetSessionData();

    GXVAL val;

    if((hr=pSessVList->GetVal("MYSESSIONDATA", &val))==

    GXE_SUCCESS)

    {

    GXObject *pWrappedObject=val.u.punkVal;

    MyClassName* pMyHeapObjectInstance=

    GX_LOCALSESSION_CLASS_UNWRAP(MyClassName, pWrappedObject);

    }


Example Code Walkthrough
In the Netscape Extension Builder sample directory is a subdirectory named Auction, which contains a Java extension that demonstrates the use of state and session management. The Auction extension is a complete example.

The sample application simulates an online auction. On the auction home page you can perform any of the following tasks:

To manage state and session data, perform the following steps:

  1. Implement the IState2 Interface
  2. You add this code in the service module's manager class.

  3. Implement the ISession2 Interface
  4. You add this code in the class representing a user.

Implement the IState2 Interface
The manager class is CAuctionModule and is represented by this file:

sample\auction\java\Auction\myext\CAuctionModule.java

The complete contents of this file, including additional comments, are as follows:

//

// This file is initially generated by KIDL - Edit as

// necessary to complete

//

package Auction.myext;

import com.kivasoft.*;

import com.kivasoft.util.*;

import com.kivasoft.types.*;

import Auction.*;

public class CAuctionModule extends

com.kivasoft.bind.BinderBase

implements com.kivasoft.IModule, Auction.IAuctionModule

{

public com.kivasoft.IContext m_Context;

public static final String m_extensionNodeName =

"NASExtensionNode";

public static final String m_auctionNodeName =

"AuctionExtensionNode";

private IState2 m_auctionState;

private IAuctionPool m_auctionPool;

public CAuctionModule()

{

super();

m_auctionState = null;

m_auctionPool = null;

}

public void finalize()

{

super.finalize();

//***

//*** Add your own code to the destructor here.

//***

}

// use init to set up the state information

public synchronized int init(com.kivasoft.IObject obj) {

int res;

res = super.init(obj);

com.kivasoft.util.Util.dictionaryPut(obj, "Auction.IAuctionModule",

this);

m_Context=(com.kivasoft.IContext) obj;

IModuleState2 stateModule = (IModuleState2)

((IDictionary)m_Context).get("com.kivasoft.IState2Module");

if (stateModule != null)

// get a root node

{

IState2 extensionState =

stateModule.getStateTree(m_extensionNodeName,

GXSTATE.GXSTATE_LOCAL);

if (extensionState != null)

// create an application-specific node

{

IState2 auctionState =

extensionState.createStateChild(m_auctionNodeName,

0, GXSTATE.GXSTATE_LOCAL);

if (auctionState != null)

// populate the state data with merchandise names

// and minimum bid prices

{

IValList merchandiseList =

auctionState.getStateContents();

merchandiseList.setValInt(new

String("15 Inch Monitor"), 250);

merchandiseList.setValInt(new

String("PCS Phone"), 190);

merchandiseList.setValInt(new

String("GPS System"), 1200);

merchandiseList.setValInt(new

String("Web Browser"), 0);

merchandiseList.setValInt(new

String("Pebble Beach Vacation"), 1500);

merchandiseList.setValInt(new

String("DVD Player"), 150);

merchandiseList.setValInt(new

String("Digital Camera"), 300);

auctionState.setStateContents(merchandiseList);

auctionState.saveState(GXSTATE.GXSTATE_LOCAL);

m_auctionState = auctionState;

}

}

}

return res;

}

// return the state interface

public IState2 getState()

{

return m_auctionState;

}

// Helper functions

// Method bodies for interface: IAuctionModule

public Auction.IAuctionPool createAuctionPool()

{

// one pool only

if (m_auctionPool != null)

return m_auctionPool;

else

{

CAuctionPool auctionPool = new CAuctionPool(this);

return auctionPool;

}

}

public Auction.IParticipant getParticipant(

java.lang.String UserId)

{

CParticipant user = new CParticipant(this, UserId);

return user;

}

// if a bid is made, update the state data for the

// corresponding merchandise

public void bid(Auction.IBid bidorder)

{

IValList merchandiseList = getState().getStateContents();

merchandiseList.setValInt(bidorder.getName(),

bidorder.getPrice());

getState().setStateContents(merchandiseList);

getState().saveState(GXSTATE.GXSTATE_LOCAL);

}

// remainder of this file is automatically generated

public static final String

appNameLocal="CAuctionModuleL";

public static final String

appNameDistributed="CAuctionModuleD";

public static final String

appNameCluster="CAuctionModuleC";

public ISession2 getThreadSession(int sessionType)

{

String appName;

ISession2 session=null;

int dwflags;

if ((sessionType & GXSESSION.GXSESSION_LOCAL) > 0)

{

appName = appNameLocal;

dwflags = GXSESSION.GXSESSION_LOCAL;

}

else if ((sessionType & GXSESSION.GXSESSION_CLUSTER) > 0)

{

appName = appNameCluster;

dwflags = GXSESSION.GXSESSION_CLUSTER;

}

else

{

appName = appNameDistributed;

dwflags = GXSESSION.GXSESSION_DISTRIB;

}

IModuleExtensionData extData = (IModuleExtensionData)

((IDictionary)m_Context).get

("com.kivasoft.IModuleExtensionData");

if(extData!=null) {

String sessionID = extData.getSessionID(null);

IModuleSession modSession = (IModuleSession)

((IDictionary)m_Context).get

("com.kivasoft.IModuleSession");

ISessionGroup sessionGroup =

modSession.getSessionGroup(appName);

session = sessionGroup.getSession(sessionID, null, 1);

if (session == null) {

// sync with application's session

int tmpflags = extData.getSessionFlags(null);

if ((tmpflags & GXSESSION.GXSESSION_TIMEOUT_CREATE)

> 0)

dwflags |= GXSESSION.GXSESSION_TIMEOUT_CREATE;

else

dwflags |= GXSESSION.GXSESSION_TIMEOUT_ABSOLUTE;

if ((tmpflags & GXSESSION.GXSESSION_PERSISTENT) > 0)

dwflags |= GXSESSION.GXSESSION_PERSISTENT;

int timeout = extData.getSessionTimeout(null);

session = sessionGroup.createSession(dwflags,

timeout, null, sessionID);

}

return session;

}

return null;

}

}

Implement the ISession2 Interface
In this example, the Participant class corresponds to an individual user of the auction application. The Participant class is represented by this file:

sample\auction\java\Auction\myext\CParticipant.java

The complete contents of this file, including additional comments, are as follows:

//

// This file is initially generated by KIDL - Edit as

// necessary to complete

//

package Auction.myext;

import com.kivasoft.*;

import com.kivasoft.types.*;

public class CParticipant

implements Auction.IParticipant

{

public Auction.myext.CAuctionModule m_Module;

private String m_userId;

public CParticipant(Auction.myext.CAuctionModule module,

String userId)

{

m_Module=module;

m_userId = userId;

}

public void finalize()

{

//***

//*** Add your own code to the destructor here.

//***

}

// Method bodies for interface: IParticipant

// Get a list of your bids

public com.kivasoft.IValList getBiddingList()

{

ISession2 session = m_Module.getThreadSession

(GXSESSION.GXSESSION_DISTRIB);

IValList list = session.getSessionData();

return list;

}

// Put an IBid object into a session

public int submitBid(

Auction.IBid bid)

{

ISession2 session = m_Module.getThreadSession

(GXSESSION.GXSESSION_DISTRIB);

IValList list = session.getSessionData();

// Put data into an IValList.

// For this example, assume the name of

// each merchandise item is unique

list.setValInt(bid.getName(), bid.getPrice());

// Assign the IValList to the session

session.setSessionData(list);

// Save the session data

session.saveSession(GXSESSION.GXSESSION_DISTRIB);

// Submit the bid to the Manager class

m_Module.bid(bid);

return 0;

}

public Auction.IBid newBid(

java.lang.String name,

int price)

{

CBid bid = new CBid(m_userId, name, price);

return bid;

}

}

 

Copyright © 2000 Sun Microsystems, Inc. Some preexisting portions Copyright © 2000 Netscape Communications Corp. All rights reserved.