projectodd/wunderboss

Abstractions

jcrossley3 opened this issue · 6 comments

I know you guys have given a lot of thought (at least, more than me) to the wboss model, and I'm sorry for taking a dump on it, but I'm uncomfortable with Applications, Components, and Containers. I would like to consider Languages/Runtimes implementation details and hide them for the common case, which I expect to involve ONE language.

Because it's only fair that you get to dump on something I suggest, here goes: an application becomes a stack (some sort of sorted map) of configured components where insertion order defines dependency relationships, e.g. you must insert a WebComponent before a RingComponent, because the components will be started in insertion order, and stopped in reverse.

So whatever stack you define is what you start and stop. No need for ComponentInstances.

There needs to be some way to discover and reflect what components are available for stacking. I'm not sure what that looks like, but I'd prefer not to call it a container. :)

Dump away!

qmx commented

does this stack idea allows stuff to be added in parallel?

I don't know. What kind of stuff? I guess I was thinking of a single stack as synchronously started/stopped, but I suppose you could get clever about parallelizing them. At that point, you've kinda implemented jboss-msc though.

I was drunk when I submitted this, btw.

I could buy into hiding Languages and Runtimes, since you're right that the common case (and perhaps the only case we support outside of running in WildFly) is a single language / runtime. I don't see how that relates to your later ideas about getting rid of ComponentInstances and a stack of configured components.

With the changes @tobias made to locating Components / Languages from the classpath, I wouldn't want to depend on the order these are registered for anything. With his changes, users don't have to explicitly register any Component - instead they just make sure they're available on the classpath (via clojure project deps, bundler entries in Ruby, etc) and use them.

I'm not entirely happen with the ComponentInstance abstraction and very open to changing that. However, I think we need some object that represents the thing that was started, whether that thing is a web context, a messaging queue, a scheduled job, etc. That's what ComponentInstance currently does.

I hear ComponentInstance and possibly parallelization and yes I think you're building MSC Service.

Sent from my iPhone

On Mar 5, 2014, at 8:40 AM, Ben Browning notifications@github.com wrote:

I could buy into hiding Languages and Runtimes, since you're right that the common case (and perhaps the only case we support outside of running in WildFly) is a single language / runtime. I don't see how that relates to your later ideas about getting rid of ComponentInstances and a stack of configured components.

With the changes @tobias made to locating Components / Languages from the classpath, I wouldn't want to depend on the order these are registered for anything. With his changes, users don't have to explicitly register any Component - instead they just make sure they're available on the classpath (via clojure project deps, bundler entries in Ruby, etc) and use them.

I'm not entirely happen with the ComponentInstance abstraction and very open to changing that. However, I think we need some object that represents the thing that was started, whether that thing is a web context, a messaging queue, a scheduled job, etc. That's what ComponentInstance currently does.


Reply to this email directly or view it on GitHub.

Good point about the auto-discovery of components. That leans me toward wanting each module (messaging, web, scheduling, caching) to have a single Java interface through which representations of "the thing that was started" are obtained.

That would obviate both the string keys and the ComponentInstances, I think.

Ben, Jim, and I had a discussion about this, and this is my attempt at
capturing/distilling what we covered.

Given that the requirements have evolved from what we originally
assumed, we can greatly simplify our model. We can drop the concept of
containers and applications, and use something like what's outlined
below.

But first, the current assumed requirements:

  • Generally one language per application. It may be possible to manage
    other lang runtimes from the primary, but that won't be encouraged
    (or even supported?) If the user wants multiple langs, they need
    multiple apps. If those apps need to share resources/coordinate,
    they need to be running in WF.
  • Any number of component instances per application. This allows you
    to have multiple undertows on different ports in the same app, for
    example.
  • Each component will attempt to adhere to a spec where possible, or
    at least a common set of functionality.

The API outlined here is generally for consumption by TB/Immutant
devs, and not generally for end-user usage (though that wouldn't be
prevented).

Now, examples of usage from ruby:

web = WunderBoss.component('web')
web.configure(port: 8080, host: 'biscuit')

The returned web object implements two interfaces:

  • Component, with #configure, #start, #stop, #name(?), #backingObject (needs a better name)
  • WebComponent, with #startServlet, #stopServlet

(apparently I need a line here to keep markdown happy)

# start may be optional for some components if they start lazily
web.start 

web.name # => 'default'

web.startServlet('/', servlet_object) # may call start if not started

web.configure(a_hash) # throws IllegalStateException: component already started

web.stopServlet('/')

# stop stops all servlets for you
web.stop

# stop stops all components for you
WunderBoss.stop 

You can break through the API and get access to the underlying impl if need be:

undertow = web.backingObject

undertow.whateverDeploysAHandler(some_args)

You can create additional components of the same 'type', just by giving a name:

# web2 will be the same instance as web, the 'default'
web2 = WunderBoss.component('web')

# a new component
web3 = WunderBoss.component('web', 'some-name')

web3.name # => 'some-name'

On the java side, we have:

A WunderBoss class. Static methods for global component lookup,
component provider registration. Languages hang here as well, but may
not be relevant to this design atm.

ComponentProviders can be be auto-discovered via wunderboss.properties
files on the classpath.

WunderBoss has a registry that is used to store whatever, including
created components. This can be used to pre-load WunderBoss with the
proper components inside WildFly.

Components are stored under "#{type}:#{name}", so the default web
component is "web:default".

class WunderBoss {
  static Component component(String type);
  static Component component(String type, String name);
  static void registerComponentProvider(String type, ComponentProvider cp);
  static void put(String name, Object obj);
  static Object get(String name);
}

The ComponentProvider interface. Knows how to create components of a
single type. There would be a CP for web, a CP for scheduling, etc.

interface ComponentProvider {
  Component newComponent();
}

The Component interface.

interface Component {
  Component configure(Map opts);
  // start, stop may return booleans?
  Component start(); 
  Component stop();
  String name();
  Object backingObject();
}

Various interfaces specific to each component type: web, scheduling, etc.