Eliminating UnsupportedOperationExceptions in adapters

James Bowes jbowes at redhat.com
Fri Jul 22 13:07:56 UTC 2011


Hi all:

While working on the katello related auth bits, I got annoyed by having
to throw UnsupportedOperationException from all of the methods, and
started looking at how we might refactor to replace those with multiple
interfaces for our adapters.

In summary, the UserServiceAdapter interface defines a number of calls
to perform CRUD operations on users (and roles). The default adapter,
which stores users in candlepin's db, implements all of the methods. The
katello adapter implements _none_ of them (all of the auth logic is
handled in katello, before a call reaches candlepin). The adapter used
by rhn.redhat.com does not implement creation/deletion. Any
unimplemented calls throw UnsupportedOperationExceptions. Other adapters
follow the same pattern (in particular, the ProductServiceAdapter).

Current practice is to have a caller be aware of which methods might not
be implemented, and have them catch the exception and notify the api
user as appropriate. Not all callers do this; its understandably hard to
keep track of this convention's usage. In actual deployments, the REST
endpoints that expose apis which hit these conditionally implemented
methods are masked off via proxies, or responded to by katello, etc.
If your deployment does not have an adapter that responds to the create
user call, you don't expose POST /candlepin/users.

For a POC, i've taken the ProductServiceAdapter and ripped out all of
the methods that wouldn't be in the rhn.redhat.com adapter into a new
interface, WriteableProductServiceAdapter (I love the name, too). In the
main code base, the DefaultProductServiceAdapter just implements both
interfaces.

Going further up the stack, REST operations that use the new
WriteableProductServiceAdapter calls get moved to
WriteableProductResource (but are still exposed under /products, at the
same urls).

Lastly, the guice calls to bind() both the new interface and the new
resource are put into their own guice module, StandaloneConfig, which is
not read by default at candlepin startup (you must tell candlepin to
load it via the module.config config directive). If StandaloneConfig is
read at startup, then candlepin will respond to product creation rest
calls; if not, then the client will get a 404.

My hope is that we can do the same for all adapters: pull out any calls
that are commonly not implemented, put them under their own interface,
and extract out a new resource. For each deployment style, we can then
write a new guice module that will be responsible for loading the
implementations of all adapters they use, and the common resource
frontends as applicable (to make it clear, WriteableProductResource
would be the same code across all deployment types, it might just not be
used at all, if they don't implement the required adapter).

Please have a look at the adaptergate branch and let me know what you
think of this approach. Is it uglier than the current style? Do we want
to change the exposed urls or have them always exist, but just throw
errors?

Lastly, even if we don't use this, it's kind of a neat POC of how we
could do 'plugins' to provide api extensions.

-James
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 198 bytes
Desc: not available
URL: <https://lists.fedorahosted.org/pipermail/candlepin/attachments/20110722/e62ba967/attachment.sig>


More information about the candlepin mailing list