Warning
This is a Draft
Background Information
Let me start out by saying this proposal doesn't propose that support be dropped for IMultithreaded interfaces. This proposal does propose that they should be marked as deprecated and over time channels should be rewritten to use IChannel instead. Eventually there might come a time when most current IMultithreadedChannel implementations have dropped IMultithreaded and we can simplify and improve the performance of uPortal by dropping support for this interface, but there are no near term plans to drop that support. Also, we need to make it clear that IMultithreaded* channels shouldn't be perceived as a better, faster more optimized way of developing a channel.
Drawbacks of Implementing the IMultithreaded* Interfaces
Complexity
Channels implemented as IMultithreaded* are much more complex than normal IChannels with the same features. This is a serious problem in an open source project. It is a serious barrier to collaboration, and this reason alone is enough to warrant deprecation.
IMultithreaded re-invents Statics
An obbject keyed off of a classname, such that only one such object for a classname can exist in a given Classloader, is a static field. This is what a static field is - an object keyed off of a classname.
IMulithreaded re-invents this. IMultithreaded asks the portal to guarantee that all boxes using this channel will be backed by the same IMultithreaded instance, such that the instance fields in that channel instance are shared across all usages of the channel. This is completely equivalent to writing an IChannel and placing those shared objects in static fields. It's an increasing best practice to avoid the use of static fields, which are well understood – it should be even more a best practice to avoid the use of an equivalent facility that is much less well understood.
No Session Replication
The static scoped state map won't be serialized and replicated so the channel will reset or display with an error once serialized and reloaded.
Potential Memory Leaks
They require the channel programmer to code the channel to remove objects from the state map when a user's session is removed. This easily results in a channel that leaks memory if implemented incorrectly.
Less Diagnostic Information
IMultithreaded* channel's also can't print out much useful information in their toString() methods to let us know what is going on when a
channel fails to render. IChannels can provide all the relevant information needed to diagnose a rendering failure including which user
the problem occurred with and what the publish/subscribe id of the channel is. The IMultithreaded* interfaces would need to be modified to have a method
toString(uid) that would take the user's session/uid as a parameter in order to print out the same useful information.
More, not less, object creation
Use of IMultithreaded* channels leads to more, not less, object creation in a running uPortal instance. This is because ChannelRenderer knows only how to render IChannels. All IMultithreadedChannels must be wrapped in IChannels in order to be rendered. So, for each box in uPortal represented by an IChannel, we have one IChannel instance. And for each box in uPortal represented by an IMultithreadedChannel, we have an IChannel instance (the wrapper) and the IMultithreadedChannel instance. IMultithreadedChannels typically include a Map of ChannelState instances. For each box in uPortal represented by an IMultithreadedChannel, we have one such ChannelState instance.
The result is more, not less, object creation. Creating objects isn't actually that expensive and this factor-of-two additional object creation almost certainly doesn't matter, but it is clear that IMultithreaded increases, rather than reduces, object creation overhead.
Additional locking and unlocking, synchronization costs
What IMultithreaded does to ChannelFactory
Honoring the IMultithreaded gaurantees forces ChannelFactory to store a Map from channel class name to channel instance. It also requires ChannelFactory to lock on channel instantiation while it detects whether the requested channel is IMultithreaded, whether it is in the Map if it is, and while it instantiates it if it either isn't IMultithreaded or is IMultithreaded but is not yet instantiated. This results in ChannelFactory serializing (with lock acquisition and release) instantiation of all channels, not just IMultithreadeds. Were ChannelFactory not supporting IMultithreaded, this locking would not be required and channel instantation could be serviced by multiple threads concurrently.
Since IMutlithreaded* is a published and commonly used interface, uPortal cannot in the near term realize the efficiencies of dropping support for this interface.
Summary
What makes this even more clear-cut for me is that we haven't written a single channel that benefits from being implemented as IMultithreaded*. It is clear that we should discourage implementation of channels as IMultithreaded*, and I think deprecating the interfaces is the clearest way to communicate this without ambiguity.
Proposal Implementation
- Mark all IMultithreaded* interfaces in uPortal 2.4.-patches, 2.5-patches and 2.6 as deprecated.
- IMultithreadedCacheable
- IMultithreadedChannel
- IMultithreadedCharacterChannel
- IMultithreadedDirectResponse
- IMultithreadedMimeResponse
- IMultithreadedPrivileged
- Discourage use of these deprecated APIs
- Refactor the core channels to implement IChannel* .
- These framework channels implement one of the IMultithreaded* interfaces:
- CGroupsManager
- CPortletAdapter
- CWebProxy
- CApplet
- CGenericXSLT
- CImage
- CInlineFrame
- CPersonAttributes
- These framework channels implement one of the IMultithreaded* interfaces: