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 as appropriate IMultithreaded channels should be rewritten to use IChannel insteadrefactored as Channels or Portlets. 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.
Advantages of Implementing the IMultithreaded* Interfaces
Reduces calls to class.instanceOf()
IMultithreaded* allows the running portal to minimize the number of calls to class.newInstance(), because this call happens once per channel class name and then, for IMultithreadeds, the instantiated class instance is stored in a static Map so that from then on it can be looked up by String class name. Map lookup, wrapper instantiation, and channel state object instantation do not add up to quite as much performance cost as does the class.instanceOf() excecution. However, the performance difference is negligible, may be outweighed by the additional runtime costs of IMultihreaded in channel state map synchronization and access costs, and all of this will be drowned out by the other costly operations involved in running the portal (executing transforms, database access, serializing the output, doing whatever valuable thing your channel does, etc.)
Drawbacks of Implementing the IMultithreaded* Interfaces
...
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
...
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.
...
IMultithreaded* channels also can't expose 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, and then code using this method would need to be aware that it was using an IMultithreaded and the appropriate key. This highlights that "IMultithreadeds are weird, not behaving like normal Java objects."
...
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.
Supporing IMultithreaded also complicates ChannelFactory, with its logic tree and instanceof checks on the channel being instantiated to see whether and what IChannel-implementing-wrapper to use around the channel.
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
...
IMultithreaded makes it hard to talk about uPortal
I don't mean this in jest. Writing this document is hard. Where I'd like to talk about "channel instances", I can't, and instead have to invent "channel usages", because IMultithreaded uses a single object instance with a keyed internal map to represent many boxes across many users' layouts. IChannel uses one object instance to represent one box in one user's layout. This is more articulable, easier to understand. Simplicity is worth something.
Alternatives to using IMultithreaded
IMultithreaded re-implements statics
IMultithreaded provides a way to share state across the objects backing boxes rendered in users layouts (the usages of the channel). Using IMultithreaded is entirely equivalent to using IChannel with static fields for state desired to be shared across all instances of the channel. Any given IMultithreaded* channel can be refactored as an IChannel with its instance fields become static fields.
Share services, not channel instance state
Including a Map with everyone else's ChannelState objects doesn't help IMultithreaded service my request. Everyone else's state is irrelevant to my request and it's just junk to be carefully ignored in a threadsafe, synchronized way.
However, it is entirely plausible that there are some things that can be shared across channel instances. An efficiency might be to lookup a portal.properties property once rather than once per channel instance. Or an Announcements channel implementation should be backed by an Announcements service rather than stuffing your institution's entire Announcements infrastructure into the channel implementation.
And these services can be shared. They can be grabbed from Factories using static initializers if they need to be shared statically. They can be grabbed from Spring if you'd like to declaratively configure them. They could even be grabbed from JNDI. Writing traditional IChannels, rather than IMultithreadeds, does not by any means mean that your channels cannot share the state and services they ought to share. Avoiding IMultithreaded does, however, help channels to avoid sharing state they don't need to share, state that just gets in the way.
Summary
There are no important advantages of IMultithreaded* to be had, and there are real costs of implementing this interface. 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
- Apply patch attached to UP-1362: Allow multiple threads to concurrently instantiate channels
- Mark all IMultithreaded* interfaces in uPortal 2.4.-patches, 2.5-patches and 2.6 HEAD as deprecated.
- Discourage use of these deprecated APIs
- Refactor the core channels to implement IChannel* .
- These framework channels implement one of the IMultithreaded* interfaces:
- CGroupsManager (Patch attached to UP-1371)
- CPortletAdapter (Patch attached to UP-1300)
- CWebProxy (Patch attached to UP-1302)
- CApplet (Patch attached to UP-1369)
- CGenericXSLT (Patch attached to UP-1303)
- CImage (Patch attached to UP-1367)
- CInlineFrame (Patch attached to UP-1368)
- CPersonAttributes (Patch attached to UP-1370)
- These framework channels implement one of the IMultithreaded* interfaces:
- Adopt a policy that no new channels implementing IMultithreaded*will be accepted into the framework (uPortal source tree) if they implement IMultithreaded*
- Propose a presentation for JA-SIG Vancouver conference "Why IMultithreaded is Deprecated and How to Refactor Your Channels away from IMultithreaded"