We could call it api, instead of spec, so as not to confuse the Jini
Spec? (We call River an implementation of the Jini Specifications)
Implementation jar: service.jar
API jar: service-api.jar
Download jar: service-dl.jar
We could also specify that new API versions should be backward
compatible: Extend or replace Interfaces, rather than breaking them.
API's should never be broken, they can be replaced, giving time for
others to transition. New versions that break an API by mistake, should
probably be removed, it's easy to do, you've only got to change a method
parameter type (even to a compatible super type) and the method
signature breaks compatibility. It still compiles and works for all
your tests, but breaks for previously compiled code.
When designing API, I've found if you can use a supertype or interface
type in parameters, then it's good practise to do so, provided it
doesn't create heaps of instanceof calls in client code (it's ok in
implementation code though).
I'm thinking if we all put our heads together here with good Service API
design recommendations for new users to follow, then by avoiding the
pitfalls, overall it will increase River's success based on their
perceived notion of River from their experiences.
I'll start writing a document if you'd all like to contribute your ideas?
Regards,
Peter.
Dennis Reedy wrote:
On May 20, 2010, at 1136AM, Greg Trasuk wrote:
Hi all:
See comments interspersed...
On Thu, 2010-05-20 at 11:05, Dennis Reedy wrote:
So this is what I'm understanding thus far:
Extend the jar conventions of service construction to include:
Implementation jar: service.jar
Specification jar: service-spec.jar
Download jar: service-dl.jar
That's way I've been doing interfaces for years, except I call it
'service-client' rather than 'service-spec'. Same thing. The client of
a service compiles against 'service-client.jar', which contains only the
interfaces and possibly any classes used in parameters and return values
on the interfaces. Then the 'service-dl.jar' (containing things like
smart-proxy code if there is any), along with 'service-client.jar' is
included in the service's codebase annotation, and the 'service.jar' is
used by the implementing container for the private implementation.
Yep, this would just formalize what you're already doing.
Along the same lines, I've always made it a habit to declare service
methods to throw IOException rather than RemoteException, so as not to
impose any dependency or implication of the RMI protocol. Since
RemoteException extends IOException, an RMI implementation compiles and
works just fine, but the client doesn't then depend on RMI.
Good idea.
(I would love to see the jars also follow the convention of including the
version number as part of the jar name, makes it clearer what is being used.
Not a mandate, just a nicety)
Clients would include direct dependencies on service-spec.jar, including that
jar as part of it's own classpath, and would also be able to provision the
service-dl.jar. The service-dl.jar has a dependency on service-spec.jar (but
not the other way around), as does the service.jar. Note that service-spec.jar
may be project wide (like River) and include service interfaces
(specifications) for multiple services, or it may be service specific.
Provide a ServiceDLEntry (implements ServiceControlled) that includes the DL jar(s) and accompanying message digests for the jar(s) allowing clients to optionally download and install DL jars to avoid expensive http(md) based class loading.
Assuming that Reggie has to unmarshal the proxy in order to return the
service item which includes your ServiceDLEntry, you could probably
accomplish the same thing by creating a class loader that maintains a
persistent cache of jar files. Either way, you're going to download the
jar file at least once,
Good point.