Upgrading to 0.9, some random notes

Here are some random notes from me on changes to the APIs in 0.9. These are now the stable APIs and baring any major problems, are not likely to change out from under you again:

(0) SimTask is gone. It is now replaced by AppContext and the various managers. See the server tutorial doc for more details.

(1) The timer and PDTimer are gone. The facilities of both are now handled by the task manager. Integrating timing into our task queuing directly allowed us to significantly improve timer steadiness and reliability.

(2) GLO/GLOReference are now called ManagedObject/ManagedReference

(3) There is now no need to explicitly call a create method for new ManagedObjects (GLOs), instead, the act of obtaining a reference to them or binding a name to them does an implicit create. Be aware that libraries can do this and thus may inadvertently stick your ManagedObject in the object store for you. For this reason its probably a good idea to avoid creating ManagedObjects that you don’t intend to go into the Object Store.

(4) In many cases where we used to pass ManagedReferences (GLOReferences) to system functions we now pass the ManagedObject (GLO) itself. The code internally uses DataManager.createReference to get references to hold onto. We found this API switch to be easier and “less futzy” then making you pass us ManagedReferences.

(5) The examples directory is gone, repalced by the complete client and server tutorials.

(6) There have been a great many internal changes as experts in all the various disciplines represented by the SGS went over everything in my prototype EA version. The result should be better, smoother operation.

Thats all I can think of off the top of my head. If you find other changes feel free to post to this thread.

Some gotchas I’ve found

Inner classes can’t be used in server side code as they fail going in or out of the store, even public named ones.

SGS still hides your exceptions, my best friends mr null pointer and mr array index out of bounds get converted in to some message in the logs about a transaction throwing, no mention of what it throws, or where.

Endolf

Yes, good point. We advise against inner classes in ManagedObjects because the semantics of inner classes in Serlizable objects are in-obvious and problematic. This is mentioned in the “best practices” section at the end of the server app tutorial.

On the exception, thats troubling. We were specificly watching for such cases this time around.

Can you send me a specific example of the code thats causing the exception and what the log reports for an exception?

Thanks

JK

That will teach me for jumping in without reading the docs fully :slight_smile:

I found the issue with the exceptions, I apologise, it was my own log formatter hiding the darn thing.

Cheers

Endolf

The server can send to a single client, or a group, or all clients on a publish/subscribe channel, clients know it was the server from a null session id, can they respond on the channel to the server, or do they have to use the out of channel communications?

I create a channel for a sector in my game, and I send a mix of updates, some are broadcast, some are unicast. The broadcast ones generally need no client responce, but my unicast messages sometimes do. I’d like to keep all of those messages on the same channel so my client needs to be able to respond to the server, but only the server.

If I use a session id on the channel as the ‘to’ session id then I get a null pointer.

Is it possible?

Cheers

Endolf

So, the answer is yes and no.

The server can evesdrop on a single channel member, or an all communications on the channel, by the use of listeners.

HOWEVER you want to be really really wary of having the server listen to chatty communications.

In general its probably best to use sendToServer. This ofcourse means that, if you are using the app as a match maker with many game sessions, those packets will have to be labeled with something to identify the game session the packet refers to.

Moved by operator to “Why Byte Arrays?”

Some random notes on channel message life cycle

Messages sent from a client via the channel will be processed on the server before being sent to the other clients attached to the channel. Channel messages will first be run through the channel listener attached during channel creation. It will then pass through the ChannelListener attached when the sending user joined the channel. No other user’s server side channel listeners will receive the message.

ChannelListeners attached when a user joins a channel are only called when THAT USER sends a message on the channel and then only if sent from the client tier and only after the ChannelListener added during channel creation is called.

If any of the server side channel listeners throw a non-retryable exception then message delivery is terminated at that point. I am guessing that this is not Sun’s intent but that is the effect. So if you have the need to terminate message propigation simply throw a non-retryable exception from either of the server side channel listeners.

Any comments?

heh, my comment is you’ve done a lot more exploring of the deep semantics then i have so far!

I’ll send your note to the team member that actually wrote all that for comment, though 8)

Moved to About Channels

Was running some tests on the DataManager. Apparently, if you create a managed object and then also bind that managed object with a name using the setBinding method, and then finally later you call removeObject() supplying that managed object it will also clear the named binding.

Aka you do not need to worry about cleaning up named bindings. All you have to do is remove the object itself and the named bindings will be cleared for you…

This is particularly of interest if you wish to implement your ChannelListeners as Managed objects and you want to bind them with a name. When the channel is closed or the user leaves the channel the listener (aka managed object) will be removed from the data store. And as such any named binding you may have had as well.

Additionally, if you hold a ManagedReferences to a managed object that was removed explicitly via the removeObject method, all future calls to get() or getForUpdate() on that reference will throw an ObjectNotFoundException.

I may be restating what is already understood, but allow me to clarify just to be sure…

The name binding actually remains in the system. It is just no longer valid. if you want to avoid collecting cruft in the name table part of your database, a best practice is explicitly removing it.

Managed Objects are “real objects” in the simulation sense. The do not exist until explicitly created, exist in one and only one state at any given instance, and exist until explicitly destroyed.

The only way to remove a managed object from your object store is with an explicit call to the DataManager to do so.

There is a fine point here in that you can create listeners that aren’t managed objects. If you do so then these listeners are non-persistent and will get garbage collected when they are no longer needed. They also will live in memory for their entire life cycle. Some managers take extra pains to recreate these listeners from an internal persistent record when the system goes down and comes back up, but that is up to the individual manager’s behavior. It is not a requirement.

yup.

I just wanted to clarify. If I create ChannelListeners that are ManagedObjects and don’t keep a reference around it never goes away?

The only way to get access to those listeners after they are created unless I keep a reference to them myself some how is via named binding. When the user leaves the channel it is essentially garbage. As there is no way to get a reference to it again unless I kept one originally or it is name bound. How would I go about destroying it? How would I know when to destroy it?

If ChannelListener had a method like destroy() or unlinked(), or detached() or something that got called when the user left the channelor when the channel was closed it could clean itself up. But the code that calls leave() has no way to access the Listener and the listener has no way to know the user left or the channel’s been closed as the API stands today…

Yup. This is in the tutorial.

The API takes a managed reference to it. As soon as a managed reference to it is taken, the Object Store ebcoems aware of it and starts saving state.

Put a name on it or keep a reference. As you said.

If the client drops, then you get a disconnected() callback and you can delete all listeners associated with that client.
That is the only way for a client to leave a channel other then under the control of your logic where again you can clean up.

In general though I would recommend one of two strategies though. Either:

(A) Have a Managed Object as an event listener that you use over and over

OR

(B) Dont use a managed object for the listener and it will go away when not needed.

I don’t follow you. if you need to track this, create a ManagedObject that maintains a Map and call it when you need that info. or just keep a list of the channels you are joined to on your user object.