ApplicationContext - extending for app needs

  9 posts   Feedicon  
Replies: 8 - Last Post: November 17, 2009 11:44
by: 5er_levart
showing 1 - 9 of 9
 
Posted: November 08, 2009 14:52 by Craig Ringer
While the SAF provides ApplicationContext as a way to gain access to the application's global state, it provides no way to extend ApplicationContext to handle apps that have additional global state. You're likely to land up implementing a similar facility yourself on the application instance, and calling:

MyAppContext ctx = ((MyApp)Application.getInstance(MyApp.class)).getMyContext()

where "getMyContext" is a method of your Application subclass. Or you'll land up with lots of similar accessor methods directly on the app class:

DatabaseWorker wrk = ((MyApp)Application.getInstance(MyApp.class)).getDbWorker();
DocumentList docs = ((MyApp)Application.getInstance(MyApp.class)).getDocumentList();
... etc

To me, that's clumsy. Why not let app implementors somehow extend/enhance the ApplicationContext facility to fit their needs?

(Original discussion started: here)
 
Posted: November 10, 2009 22:16 by etf
Personally I hate idea of the context subclassing. My suggestion is generic interface for initializing of the application context, plugging custom resources (objects) into it, etc. In case subclassing is the only way (developer has more control over the context), we could use something like Application.lunch(MyApplicationContext.class);
 
Posted: November 11, 2009 08:27 by Vity
+1 for etf
 
Posted: November 11, 2009 14:32 by Craig Ringer
The work being done on generic resource injection at the moment largely removes the motivation for this. If app-specific resources can be easily and conveniently injected then there's no need to extend or modify the ApplicationContext.

I do agree that encouraging subclassing ApplicationContext is risky, and actually somewhat ugly to boot. The problem is better solved through resource injection.
 
Posted: November 11, 2009 19:49 by robross
My thoughts on AppContext:

First, mentally refactor AppContext and remove the part that worries about what actual "Context" it exists in. Then, you just have a container of object resources used by an Application. In my mind, that becomes an implementation detail of Application, and there's no reason any external objects need to know how Application provides, for example, a ResourceManager, or ActionManager, when calling Application.getResourceManager or Application.getActionManager.

To add object resources, a client programmer could add these to subclasses of Application. (I know that's a different conversation, but this *is* a framework, not a library, and Application base class provides a useful framework under which to run.) Of course, then the framework user has to add new fields and accessors to the Application subclass.

Or we could just store all of Application's resources internally in a Map, and get them by name with a class constant key, which in combination with a generic method also casts it nicely for us. (My favorite solution)

I.e,

ResourceManager rm = Application.get(ResourceManager.class);
ActionManager am = Application.get(ActionManager.class);

etc. This would be easily extensible to any kind of resource object a framework user might want to add, in a type-safe way as well.

FooManager fooManager;
Application.addManager(fooManager);
...

FooManager fm = Application.get(FooManager.class);


And now, back to the "context" issue of ApplicationContext.

I think each Application should be the root node of the object graph for a particular "instance" of an application. We don't have to explicitly design for multiple Application instances, but one of the side-effects of my solution would make it trivially easy to support, either in a desktop, Applet , or JNLS context.

All you need to do is

1. Not use any singletons. Or if you really want to, load them in a custom classloader loaded by the root Application. Which is complicated. So it's easier to say, no singletons. And this is not hard to achieve, plus it has added benefits such as easier dependency injection, and unit testing with mock objects.

2. The Application becomes the top level node of the object graph for all objects in a "module" for lack of a better term. (Not that I want to go into any detail designing a module system right now.) The typical case is, One application in one JVM, loaded by one classloader. But, if we design it the way I am describing, we could actually have multiple Application instances per JVM (all with their own unique resources, no collisions, but sharing could be possible), either via loading in different classloaders, or different AppContexts (the Swing kind).

All you need to achieve all this magic is replace the singleton design of Application with :




//In Application
private static final SwingStaticProperty<Application> appInstances = new SwingStaticProperty<Application>();

...

public static Application getInstance()
{
return appInstances.get();
}


...
public static void launch(...){

//after new Application is created call
appInstances.set(app);
...


// In Sun JDK we use sun.awt.AppContext to keep global values for a context
// this class uses only public API and is introduced
// to be compatible with the third party JDK's
final class SwingStaticProperty<V>
{
private final Map<ThreadGroup, V> valueMap = new WeakHashMap<ThreadGroup, V>();

private final Object monitor = new Object();

public SwingStaticProperty()
{
}

public SwingStaticProperty(V value)
{
set(value);
}

public void set(V value)
{
ThreadGroup tg = Thread.currentThread().getThreadGroup();
synchronized (monitor)
{
valueMap.put(tg, value);
}
}

public V get()
{
ThreadGroup tg = Thread.currentThread().getThreadGroup();
synchronized (monitor)
{
return valueMap.get(tg);
}
}
}
}


That completely solves the Context problem, as long as no resources used by Application are singletons. Or if they are JVM/Swing/Java library singletons, that is acceptable, since any Applets or JNLS apps already have to deal with this as well. Also, Application cannot have any mutable static members. But mutable instance members are fine.


Thus, multiple Contexts in a JVM could each launch their own Application instance, and they would all peacefully coexist.

Also, in a single Context, multiple Applications could also exist if each Application, and all objects loaded by that Application. are loaded in separate ClassLoaders (one per Application).


We don't have to explicitly do anything to make this all possible, except what I have written above.

Rob
 
Posted: November 11, 2009 21:22 by etf
Thank you Rob for the great presentation. It looks very impressive. My only concern is dividing data and behavior. Application itself presents a behavior template, the lifecycle of the real application. The context us about data and resources. Thus I'm not sure we should get rid of context and move data to Application. What do you think about it?
 
Posted: November 12, 2009 01:42 by Craig Ringer
I share etf's preference for separating the behavour (Application) and data (ApplicationContext) here. As it is, Application is at risk of becoming the user's catch-all class for globally useful helper methods, user-added state, etc. I've had the "fun" of maintaining applications built this way before (C++ apps built with Qt by inexperienced developers) and trying to clean up a 20,000 line application class into a few functionally separated classes that can be reasonably understood and maintained is *not* fun. So, IMO the framework needs to make it as easy and natural as possible to keep things separated where possible.

As for the HashMap based context: I'm concerned that it may break polymorphism. The HashMap can't easily return a "MyBlah extends Blah" when asked for a "Blah". And, worse, it can't reasonably decide which "Blah" to return if two or more entries in the map are based on Blah. While as discussed this is completely abstract, I'd be surprised if it didn't come up in practice.

These issues don't apply if you have a 1:1 mapping from storage (say: instance variable) to explicitly typed accessor methods. The polymorphism issue can also be solved with string retrieval keys instead of class retrieval keys, since the ambiguity about which instance you're after is removed, but what vestige of type safety and compiler error checking the hashmap approach had is lost if using string keys. I've had enough "fun" with string keys dealing with JPA named queries and with PropertyChangeSupport properties to know I don't want to have to deal with them if an alternative is available.

If we don't want users directly extending ApplicationContext and don't want generic accessors based on a HashMap, though, how can the framework help users keep track of their global context without resorting to singletons or just banging more properties into their Application subclass?

I don't have any good answers. I added a resource container class to my app, much like ApplicationContext but for app-specific global resources, and gain access to it via:

((MyApplication)Application.getInstance(MyApplication.class)).getMyApplicationContext()

... which works fine, but isn't pretty. The ugliness gets hidden in a static method of MyApplication (not the real class name of course) and it's alright. It just feels like there should be a nicer way. Maybe going for a HashMap based storage option for user resources is the best choice, since after all the developer can always choose to go with something like what I've done if they don't like it - but most of the time the HashMap based storage would be quite sufficient.
 
Posted: November 12, 2009 02:20 by robross
I don't think this change introduces any new data management into Application. Currently, ApplicationContext works almost like a Service & Resource provider/locater/registry. And Application is the entry point from which to obtain these service/resource objects.

All that my proposed changes would do is remove one layer of indirection, and greatly simplify the API used to obtain these objects. Application is still ignorant of what any of those service classes do, (except for any it uses directly, like maybe ResourceManager/Map to read in properties to do things like init the L&F, etc.)

Application would just be providing the central location in the framework for obtaining these service objects.

With my changes, code like this:


ApplicationContext ctx = getContext();
ctx.setApplicationClass(getClass());
ctx.setApplication(this);
ResourceMap appResourceMap = ctx.getResourceMap();
ctx.getResourceManager().setPlatform(platform());


can be replaced by:


Application.get(ResourceManager.class).setPlatform(platform());


Or


Application.getInstance(DefaultActionsApplication.class).getContext().getActionMap();


becomes


Application.get(ActionManager.class).getActionMap();




Etc.

It looks cleaner, it's simpler to code and explain, and removes one layer of indirection that I argue adds no extra value to this framework.

Rob
 
Posted: November 17, 2009 11:44 by 5er_levart

I think that:

Application.get(ActionManager.class).getActionMap();
...

is not much better than:

Application.getInstance(...)...
...

both are ways to lookup singletons. Singletons are evil. It's better to leverage existing dependency injection frameworks to inject dependent resources, avoiding resource lookups and instead make SAF more DI framework friendly.

I'm quite successfully using SAF with Spring. I had to work arround the "Application as a singleton and it's own factory" design using hacks such as for example this:

public class SpringSingleFrameApplication extends SingleFrameApplication
{
public static final String MAIN_VIEW_BEAN_NAME = "mainView";

private String[] paths;

@Override
protected void initialize(String[] args)
{
    if (args == null || args.length == 0)
    throw new IllegalArgumentException("No arguments specified for Application.launch() method");

    paths = args;
}

@Override
protected void startup()
{
    ApplicationContext appCtx = new ClassPathXmlApplicationContext(paths, getClass());
    View mainView = (View) appCtx.getBean(MAIN_VIEW_BEAN_NAME, View.class);
    show(mainView);
}
}
...

...and then using the following to be able to "inject" Application instance into other components of my application:

public class ApplicationFactoryBean implements FactoryBean
{
@Override
public Object getObject() throws Exception
{
    return Application.getInstance();
}

@Override
public Class getObjectType()
{
    return Application.getInstance().getClass();
}

@Override
public boolean isSingleton()
{
    return true;
}
}
...

It would be nice to make BSAF more DI framework friendly than SAF. Currently it is impossible to break the singleton (I agree this is not something one would like to do). But at least it should be possible to construct new instance of Application using a DI framework of your choice.

showing 1 - 9 of 9
Replies: 8 - Last Post: November 17, 2009 11:44
by: 5er_levart
  • Mysql
  • Glassfish
  • Jruby
  • Rails
  • Nblogo
Terms of Use; Privacy Policy;
© 2010, Oracle Corporation and/or its affiliates
(revision 20120518.3c65429)
 
 
Close
loading
Please Confirm
Close