Session Manager Service

Revision as of 08:06, 29 October 2008 by Paulb (Talk | contribs) (Corrected example)

The Session Manager Service is a graphical-based service used in EiffelStudio to persist and retrieve data between multiple sessions of EiffelStudio. The session manager (SESSION_MANAGER_S) provides access to state persisted sessions in four flavors; Environment, Per-Window, Per-Project and finally Per-Window/Per-Project. The fine grained control of session states proffers a mechanism needed to restore EiffelStudio as it was left as well as restore a project to exactly how it was left when it was closed.

Session data replaces the need to define (hidden) preferences in order to persist data between running sessions of EiffelStudio, making the process of storing session data much simpler.

Preferences Vs. Session Data

The session manager service is very simple to use and supports arbitrary data types so you may want to jump right in an use it instead of the defining a configurable preference. Session data and preference data have different roles so it is important you choose the right method of data persistence for the task you are trying to perform.

Both session data and preference data are persisted between opened and closed sessions of EiffelStudio, which is the extent at which the are akin. Session data will never been seen by the user whereas preference data can be viewed and modified from within the EiffelStudio IDE. It would not be wise to store a discardable dialog prompt's discarded state in a session because the user may want to revert back to view the dialog prompt at a later time - this is a clear case for a preference. On the other end of the scale, storing a tool's tool bar toggle button's selected state would be ideal to store as session data instead of preference data. That said, storing a toggle button's state in session data might not applicable for the task it's to perform. Note the reference to a tool's tool bar button. A tool is general self-contained and should not affect the behavior of other parts of EiffelStudio. Tool bar buttons on the main tool bar, which actually affect EiffelStudio as a global entity generally should not use a session but a preference instead. The exception to this rule is if data needs to be persisted on a per-window or per-project level.

Sessions and Aggregated Sessions

Sessions come in four flavors, with each session being distinct. This could be a hassle for anyone wishing to access multiple session data, such as both the environment as well as project-level data. Fortunately most of the session data objects are aggregated to support retrieval of session data for a higher, wider scoped session. The only session that is not aggregated is the environment session as there is no wider scope that the EiffelStudio environment itself, which encompasses all opened windows.

Here is a brief run through of the level of data that can be read by a type of session:

  • A environment-level session will only provide session data exposed in the environment session.
  • A project-level session will additionally provide session data exposed in the environment session.
  • A window-level session will additionally provide session data exposed in the environment session.
  • A project and window level session will additionally provide session data exposed in the window session and then session data exposed in the environment session.

Retrieving Sessions

In order to work with a session, represented by a SESSION_I interface, you will need to query for the session manager service. This can be done using a service consumer or via accessible service provider.

There are only two function exposed on the session manager service for retrieving a session, and are used depending on what type of session needed to be retrieved; retrieve and retrieve_per_window. retrieve will retrieve an environment or project-level sessions where as retrieve_per_window is used to retrieve window or project/window-level sessions. retrieve_per_window requires an additional argument specifying the window to retrieve a session for.

The following code retrieves a session for the environment:

environment_session: SESSION_I
    -- Retrieve the environment (IDE) session.
  local
    l_sm: SERVICE_CONSUMER [SESSION_MANAGER_S]
  do
    create l_consumer
    if l_sm.is_service_available then
      Result := l_sm.service.retrieve (False)
    end
  end

For window-level sessions, retrieval is even simpler. In order to retrieve a window-level session using retrieve_per_window a client needs to have access to a valid (attached and non-recycled) EB_DEVELOPMENT_WINDOW object. EB_DEVELOPMENT_WINDOW actually exposes session_data to clients, which is the respective window-level session data.

When retrieving project-level sessions there is no need for a attached project object to be supplied, the reason is two fold: (1) EiffelStudio currently only supports a single project loaded at any one time and (2) it may be necessary to retrieve a project-level session before the project is loaded. The internals of a project session will ensure that once a project is loaded the session data is set and the appropriate events are fired.

Information.png Note: Although it is possible to retrieve a project-level session before a project is loaded, the session data will not be respective of the last persisted project. Until a project is loaded there is no way for the session manager to determine which project session data should be retrieved for. Once a project is loaded any project-level sessions will be updated with the loaded project session data. Clients can receive notification of this by subscribing to the value changed events.

For tool developers, ES_DOCKABLE_TOOL_WINDOW already exposes access to both environment and window-level session objects via session_data and window_session_data respectively.

Using Sessions

Sessions are utilized through a retrieved SESSION_I object interface. It's through the session object interface clients can retrieve or set session data. All session data interaction is performed using a session data identifier.

Session Data Identifiers

To use a session object to persist and retrieve session data clients will need to define a session data id. A session data id is a string constant used to identify chunks of session data in the session object so it can be indexed for storage and retrieval between sessions of EiffelStudio. Strings are inherently problematic for uniqueness and UUID are limiting in readability as well as prohibiting static access to a session data id, if required. As such, string id's were chosen to index session data with a recommended namespacing convention to ensure uniqueness.

Session Data Identifier Recommendations

There are no absolute rules to declaring a session data identifier except that is must be a non-empty attached string object. However to ensure uniqueness the following rules should be adhered to:

And id should...

  1. Be defined as a namespace using periods (.) to split segments, contains no whitespace and using a reversed company or personal URL domain prefix. For instance all Eiffel Software session data id's start com.eiffel.
  2. Include a context, such as the name of a tool or region inside EiffelStudio.
  3. End with a brief description of the session data.
  4. Predominantly use alphabetically characters.

As an example, for an Eiffel Software tool (the Error List) perserving the expand error description toggle button state:

com.eiffel.error_list.expand_errors

The session data id is split into; reversed URL domain: com.eiffel, tool name: error_list and a session data description: expand_errors.

Setting Session Data

Setting session data on a session object is straight forward call to set_value. set_value uses a session identifier to index the session data in the session object and the session data value. See Storing Session Data for the limitations and recommendation on the objects usable as session data.

Here is an example for setting a session object's session data:

set_my_data (a_data: INTEGER)
    -- Set `my_data' with `a_data'.
  do
    session_data.set_value (a_data, my_data_id)
  end
 
my_data_id: STRING_8 = "com.eiffel.tool.my_data"
    -- Session data id

Retrieving Session Data

To retrieve session data from a session object a client may use either value or value_or_default, using a session identifier to fetch the indexed session data.

Extending the example taken from Setting Session Data, below is an example on how to fetch a basic expanded value:

my_data: INTEGER assign set_data
    -- Access to my data.
  do
    if {ref: !INTEGER_REF} session_data.value (my_data_id) then
      Result := ref.item
    else
      Result := 10
    end
  end

In the example an INTEGER_REF is used, in an object test, to determine if the session data value was set prior allowing a client to handle cases when the value has not been set.

A simpler approach to the above example is to use value_or_default, which returns a default value if no session data was set prior:

my_data: INTEGER assign set_data
    -- Access to my data.
  do
    if {ref: !INTEGER_REF} session_data.value_or_default (my_data_id, 10) then
      Result := ref.item
    end
  end

Again INTEGER_REF is used but this time to avoid a compiler warning about attempting to reverse assign to an expanded type. To go simpler still, at the expense of a compiler warning when using expanded types:

my_data: INTEGER assign set_data
    -- Access to my data.
  do
    Result := session_data.value_or_default (my_data_id, 10)
  end

Of course, when attempting to retrieve a session data value of a reference type, there will be no compiler warning an this approach can be used effectively.

Storing Session Data

Session data, for the most part, can be scalar data or arbitrary objects but there are objects that will cause problems if they are passed as or are referenced by any session data.

What Not to Store

Expanded non-basic types will cause problems with the 6.2 development branch because of the storage mechanism used. The solution to this is currently to enclose an expanded value in a CELL object. This boxing is not requires for the basic built-in types because the storage mechanism is aware of these types and performs the boxing and unboxing as necessary.

Any reference type can by used as session data but the object should not retain any reference to any internal objects of EiffelStudio or any of it's referenced libraries, such as EiffelVision2. The current storage mechanism will try to resurrect these objects causing problems when the object in turn tries to function or perform operations using those resurrected object(s). The problem arises as the objects may not actually be tied to the environment itself and so the session and the running EiffelStudio internal states are out of sync. As a remedy for a first level of protection all session data objects have to implement SESSION_DATA_I. Any attributes of the session data object do not have to implement SESSION_DATA_I as it is likely the session data object will contain reference to rudimentary structures, so this restriction cannot be imposed.

Session data objects should also no reference any pointers from either external or internal resources. Resurrected pointers will likely point to an illegal, uninitialized memory location causing a segmentation violation when used.

Receiving Session Data Value Change Notification

Session objects can be shared amongst many parts of the EiffelStudio IDE and those parts may shared common session data. When a session's session data value changes those parts using a session may need to receive notification of the change. Each session exposes value_changed_events for clients to subscribe to, to receive change notifications and explicitly publish changes. Any differing session data value set on a session object will automatically publish the value changed event and notify any subscribers of the change.

Information.png Note: If any changes are made to attributes on an actual session data value object, and not the session data value itself, clients making the changes should publish the value changed event, for associate the session data object, explicitly as there is no way for the service manager receive automatic notification. This is important as clients may be interested in the change.

Determining the Type of Session

Every session object is different and it may be necessary to determine what kind of session a client refers to in a general manner. The SESSION_I interface exposes two methods either using a couple of queries or comparing the session's "kind".

The most natural Eiffel way to determine the kind of session a client references is to use the queries is_per_project and is_per_window. Used in unison a kind can be determined. Below is a table of session kinds represented by the return Boolean values of these two queries:

Type is_per_project is_per_window
Environment False False
Project True False
Window False True
Project + Window True True

The more concise way to determine the kind of session is using the kind function and comparing the result with the predefined "kind" identifiers in SESSION_KINDS. All kinds are represented as UUIDs.

Storing Session

Sessions can be stored either on a per-session basis or the session manager service can store all sessions. The session manager service (SESSION_MANAGER_S) interface exposes store, for storing a independent session object, or store_all for storing all applicable sessions.

Session storage is optimized only to store what needs to be stored. A session object is not stored if no changes to it's session data have been made (this can be observed by querying is_dirty on the session object's interface SESSION_I.) Even calling store explicitly, passing an unmodified session objects, will bypass storage. Session storage is also skipped if a session is never retrieved from the session manager, which is only applicable in the case of using store_all as store requires a session object.

Information.png Note: Session storage optimizations are only applicable to the default implementation of the session manager service. If the session manager service is replaced by a third-party service then the optimizations may not be performed.

EiffelStudio actually perform storage of all session when any development IDE window is closed, so there is no need to explicitly store any session, unless a cases presents itself. In such an exception case, it is up to you do decided when a session should be persisted. Any code that explicit session storage should be designed with a slower session manager in mind as other editions, even first-party future editions, may alter the persistence mechanism in any given release. A potential alternative mechanism would be persisting session data to online storage, which varies greatly in performance.