For some pooled objects it is that simple. For the LINCEnvironment object, there are several other considerations you must keep in mind.
Is the object connected to the host?
Each LINCEnvironment object can be thought of like a terminal. To send and receive messages from the Agile Business Suite Server, you must first be logged onto the host. Each LINCEnvironment object must be connected to the host prior to its use.
The important thing is that in a pooled environment you only need to connect it once. Once connected all subsequent users can take advantage of that fact. When the object is released back to the pool, it remains in whatever state it was prior to being released.
To check if an object is connected call the isConnected() method. If this returns true, then there is no more to do. If it is false, then you must connect in the usual way.
Is the user logged onto the Agile Business Suite System?
Agile Business Suite is essentially terminal based, that is, it likes every user to be recognized by saying 'HI' to it. This Hello message prompts the server to create a user record to contain information about the user, such as the current transaction number and Glb.Work.
If you intend to keep state information in your system (in Glb.Work for example), it is important every user says HI. If you have a stateless system, the HI is unimportant and can be done once per LINCEnvironment object, that is, do the HI once after the connect.
With an ASP system, you would generally do the HI when the user begins an IIS Session. This means the HI code would be in global.asa in the Session_OnStart subroutine.
Note: At this time, the keeping of state with pooled objects is only available when using the MSMQ transport. That is, it does not work when connecting via TCP/IP sockets. When using TCP/IP sockets, the Agile Business Suite System must be stateless.
For stateful systems, the host sends back a user identifier with the response to the HI.
This needs to be passed back with every subsequent transaction, so that Agile Business Suite System can re-establish the state for that particular user. The user id can be obtained by a call to the method getUserID() which returns a string.
The user identifier string can be stored in the IIS environment object if you are using a single web server. If you are using a web farm it might need to be stored as a cookie on the clients browser, this depends on what sort of web farm, and what sort of load balancing.
Before every transaction, you should re-establish the user identifier by calling the method setUserID(string) with the previously stored string. This allow the Agile Business Suite System to restore the state for that user.
What is the context of the LINCEnvironment object?
Without object pooling it is possible to assume that the context (or Ispec) of the LINCEnvironment object, is the same as the last time you used it. This is not the case for pooled objects. You cannot know what other users might have done with the object since you last used it.
This means that the context needed must be re-established every time the object is taken from the pool.
The simplest and safest way to do this, is to do a getIspec() with the Ispec that is required. This loads the IspecModel class for that Ispec and allows you to set the field values. The getIspec() does not send any request to the host. It simply creates an empty IspecModel ready for you to fill out.
Refer to How are field values set? in the following section for more information on filling out these fields.
What about the ancillary objects?
Other objects that are created by the LINCEnvironment object, such as the statusLine object, the listRepository, and any IspecModel (such as the often used 'currentIspec'), all need to be re-created or re-acquired. You should not attempt to hold onto any of these objects after releasing the LINCEnvironment object back to the pool. They are bound up with the LINCEnvironment object and do not function correctly without it.
This means you should create a new statusLine object and re-acquire things like the listRepository, which is owned by each LINCEnvironment object.
As each LINCEnvironment object holds its own listRepository object, you cannot assume that lists (particularly dynamic lists) hold the same values as they did before. That is, if a dynamic list was sent in a previous ispec, it might not be present in the 'new'
LINCEnvironment object, or it might have different data for a different user. If you use a lot of dynamic lists, you should consider always creating them in pre-screen logic, and calling loadIspec() after you do a getIspec(). This then ensures that you have the correct lists.
How are field values set?
For the most part, setting the field values is as it has always been, with one important exception.
Without object pooling, the LINCEnvironment object assumes that if the client did not set a field's value then it would send back the value it received from the host for that field. This is not possible in the pooled environment.
This means that all fields must be set by the client prior to submitting a transaction. This is particularly important for the TOP_LINE and ACTMTH fields. Without these, the Agile Business Suite System does not understand what is being sent, and responds with an unexpected message format.
What about NOE fields?
Not Entered (NOE) fields are a way of sending and receiving data in a field without letting the user alter it. This is mainly used for the TOP_LINE (the transaction header) and the ACTMTH (account month) fields. Component Enabler prevents you from assigning values to NOE fields, and always uses the data sent from the host in the first place.
This functionality would prevent object pooling, so a new method has been introduced that allows you to set values for NOE fields.
The new method is setFieldValueForced() and has the same parameters as setFieldValue().
This method should only be used on NOE fields as it bypasses some of the usual checking done on the values of fields (particularly numeric fields) and may allow invalid data to be sent to the host. |
What about transaction numbers?
Note: You only need to manipulate transaction numbers when using the MSMQ transport.The transaction number is part of the TOP_LINE field, so when you set the value of TOP_LINE (via the setFieldValueForced() method) you are also setting the transaction number. If you are writing a custom client that sends several transaction numbers in a row, you should use the transaction number returned from each transaction for subsequent transactions.
In a non pooled environment, the LINCEnvironment always remembers the last transaction number sent from the host and replaces the transaction number in the TOP_LINE field with the last one it remembered. This is for custom clients where you might be combining multiple Ispecs into one page. For instance, if you were sending back three Ispecs that make up your transaction, and the first one changed the transaction number (by doing a database update) then the subsequent Ispecs would fail with an invalid transaction number. The LINCEnvironment object prevents this by always using the last transaction number it was sent from the host.
Unfortunately in a pooled, stateful environment this does not work. Each user has their own transaction number sequence, therefore the last one that the LINCEnvironment object saw might be totally unrelated to the current user, and if used, causes the transaction to be rejected.
This means that you must manage the transaction number yourself. You must also tell the LINCEnvironment object that you do not want it to interfere with the transaction number.
To do this, call the new method setObjectPooled(true). This method tells the LINCEnvironment object to leave the transaction number alone.
SwitchTo in a pooled environment is only supported with the MSMQ transport
Currently, when a SwitchTo is actioned on the host, the connection is switched to a different system. Subsequently, all input from that connection is sent to the new system. This is disastrous in a pooled environment.
It is possible to check which system an environment object thinks it is in, and to then switch to the correct one by writing a specific Ispec to switch you back and call it. However this would likely slow your system down beyond what it would have been if you had not used object pooling in the first place.
With the MSMQ environment, the SwitchTo parameters are passed out to the client to action. There is a new Response Code of 850 (OK_MSMQ_SWITCH). When receiving this code, the client should call the new methods on the ApplicationState object to retrieve the parameters passed from the host. Using these parameters, the client should adjust the application name and queue name, and connect to the new application and queue. Once connected, the client must issue a HI, and use the new user identifier from the new system. Using the data passed from the previous system (if any) the client should navigate to the correct ispec, and pass in any other data sent.
Running multiple pools to different systems
One limitation of the COM+ pooling is that it works off the CLSID allocated by the javareg.exe program. This CLSID is based on the class name of the java class and therefore you can only ever have one CLSID for a particular class.
What this means is that if you want two pools running to two different systems, COM+ does not distinguish between them.
The simple way around this is to 'subclass' the LINCEnvironment object. This means you can access it via a different class name. It therefore gets a different CLSID when you register it and you can create different COM+ pools.
Refer to Subclassing the LINCEnvironment Object for how to do this, and for the other things you can do with subclassing.