I finally had the chance to look through the org.apache.river name
change work Dennis Reedy has done, it all looks very impressive, he's
even taken the time to tidy up the qa suite. I haven't had time to run
any tests or look at the jtreg test suite, I promise I'll make some time
in the near future. Before we release this code there is an opportunity
to tidy up the org.apache.river name space even further. In the Jini
days, com.sun.jini.* was implementation code, it wasn't part of the Jini
public API, should we now use org.apache.river.* for this purpose? There
is some new public api, in org.apache.river.api.* and at the time new
implementation code was being placed into org.apache.river.impl.* and
now the com.sun.jini.* namespace has been moved to org.apache.river.*.
Should we consider placing the new api in the net.jini.* namespace? It's
worth looking at the javadoc as most of the new classes are package
private. There are also discovery constraints in the implementation
namespace that should be moved into the public api in my opinion, thoughts?
IPv6, for River, this is big, IPv6 has auto network configuration,
powerful multicast abilities, IPSec and no need for NAT. IPv6 is going
to allow our existing discovery protocols to work over the internet.
The examples project looks promising, I like how Greg Trasuk has
structured the examples into api, server and clients, Greg has done a
lot of work to tidy this up. Our existing example code is relatively
old, I did notice some bad practices by current standards as a
consequence. If we want to reduce our support burden, we should
encourage new users to use best practise.
The issue that stuck out the most was letting 'this' escape during
construction. All River service implementations now implement the
Starter interface to avoid letting 'this' escape during construction,
however since there are a number of downstream “Container” projects and
there was controversy surrounding the start method; if someone wants to
propose something less controversial for user examples, please do so,
hopefully it won't upset anybody still clinging to unsafe publication.
On the topic of “Letting 'this' escape”, because readObject methods
behave like constructors, the jvm performs a final freeze after the
readObject method completes, however there are a number of places where
River lets 'this' escape during deserialization, I did have some
solution options for this, including a better way to deserialize...
I try not to discuss River security after another developer raised
concerns it was scaring off new developers. I'm going to take the
liberty to discuss security and performance briefly.
Some points:
*
IPv6 will enable River to traverse the internet, easily.
*
IPv6 is plug and play – autoconfiguration of network devices.
*
River is well positioned for the internet of things, but needs IPv6.
*
IPv4 NAT is pretty much what killed the Jini iot tech 20 years
ago, as Jini was distributed, not centralised, web services have
grown up around this centralised model.
*
River security isn't ready, our crypto protocols need updating and
proxy trust is currently flawed.
The issue with proxy trust:
*
We can discover a lookup service securely.
*
We can't connect to services securely, service proxy's are
downloaded and deserialized before trust is established.
Don't despair, the security issues are easily fixed.
Back in the early days, Jini used RMI and RMI used skeletons and stubs.
The stubs had to be downloaded, so you always had codebase downloads.
Now, codebase downloads aren't always required, but the lookup service
implementation, is still designed around codebase downloads.
When security was enhanced in Jini 2.0, we were given the concept of a
bootstrap proxy, which is just a reflective proxy that doesn't require a
codebase download. So what does River do, it downloads a codebase,
deserializes a service proxy and then requests a bootstrap proxy from
it. At this point in time the river client authenticates the service,
then River asks the bootstrap proxy for a TrustVerifier instance to
check the service proxy, permissions are dynamically granted (but how do
we know what permissions are required?) and method constraints are
applied. This is called proxy preparation and it's a configuration
concern, as are the exporters. Yep this is a complex processs.
How could this flaw be fixed without impacting the client?
Easy, the lookup service shouldn't contain the service proxy, only the
bootstrap proxy. Guess what, big performance increase, just like delayed
codebase downloads, thank you Gregg Wonderly, for identifying and
trailblazing that path at least a decade ago.
During proxy preparation (a process determined by configuration),
instead of asking the service proxy for a bootstrap proxy, the lookup
service should only contain the bootstrap proxy and clients obtain the
service proxy from it, after authentication, constraints are applied,
permissions granted to the proxy and the process is complete. This is
much simpler than our current proxy trust establishment. Less
serialization overhead, less network traffic, more performance. A
configuration flag could restore the old behaviour of course.
The client is none the wiser, it still receives a fully prepared and
constrained proxy. All River services already implement
ServiceProxyAccessor, although part of the start package, it's an
interface that provides access to the service proxy.
We only need a new Exporter (easy) that creates the bootstrap proxy and
ensures it doesn't implement any interfaces that would require a
codebase download.
Then when a service is registered with a lookup service, Reggie obtains
the bootstrap proxy from the exported service proxy in the client jvm.
It also means the entire process is simpler, developers no longer need
to learn the complex TrustVerifier process as it becomes an Endpoint and
system concern.
Thoughts?
Regards,
Peter.