I wanted to send an update on the work I've been doing with Tiles. I should have some code by today or tomorrow. I'm refactoring the DefinitionsFactory structure (as described below), creating a Filter that will reload the factory if it's out of date, and writing unit tests to verify all the work. In addition to unit tests I'm going to write a web app (perhaps an existing example app) that will verify everything works the same as before refactoring.

If anybody has time to review the approach I'm taking I'd love some feedback. I'm pretty green when it comes to synchronization so please let me know if my assumptions about it are incorrect. Also, I know Tiles pretty well, but I really hope I'm not changing its behavior at all with this refactoring. I plan to do more extensive testing to verify that, but there's just so many nuances to definition inheritance, I18N, etc. that I might be getting something wrong. Lastly, this refactoring does not try to preserve backwards compatibility. It should not require changes to JSPs or web applications using Tiles out of the box. But if anyone has customized the DefinitionsFactory structure rework would be required to use my mods. If I need to support backwards compatibility I'll take that into consideration. But since Tiles is a new standalone project, I'm not sure if backwards compatibility is an issue. Please let me know what you think. Now... on to the details:

The goals of the refactoring are:

* Separate the concern of reading XML from the DefinitionsFactory structure. * Separate the object graph of ComponentDefinitons from the factory which reads and updates it. * Add a facility to reload the ComponentDefinitions if the config sources have changed.

The problem of separating the object graph from the factory is that both have to be stored in application scope and both will have to be synchronized when they are being updated. But, the object graph does not have to be synchronized while the factory is being updated -- if that makes any difference. Here's the basic flow of the Reload Filter

    ComponentDefinitionsSet definitions = (ComponentDefinitionsSet)
        servletContext.getAttribute(KEY);
    DefinitionsFactory factory = (DefinitionsFactory)
        servletContext.getAttribute(OTHER_KEY);
    if (factory instanceof ReloadableDefiniitionsFactory) {
        if (((ReloadableDefinitionsFactory) factory).refeshRequired()) {
            synchronized (factory) {
                ComponentDefinitions newDefs =
                        factory.readDefinitions();
            }

            synchronized (definitions) {
                definitions.reset(newDefs);
            }
        }
    }



The revised DefinitionsFactory interface looks like this:

public interface DefinitionsFactory {
  public void init(Map params) throws DefinitonsFactoryException;
  public void addSource(Object source);
  public ComponentDefinitionsSet readDefinitions()
        throws DefinitionsFactoryException;
  public void addDefinitions(ComponentDefinitionsSet definitions,
        Locale locale)
        throws DefinitionsFactoryException;
}

The default implementation is UrlDefinitionsFactory. It takes URL-based sources and reads Tiles configuration via a DefinitionsReader interface (described later). It has the following properties:

* addSource takes an URL object that represents a base XML file (tiles-defs.xml). Internally, it takes an InputStream from this URL to pass to the DefinitionsReader.

* readDefinitions calls the DefinitionsReader for each source (XML file) and returns a consolidated object graph.

* addDefinitions takes an existing object graph and a locale, reads localized definitions, and adds them to the object graph.


A factory signifies it is reloadable by implementing the ReloadableDefinitionsFactory interface. It is as follows:

public interface ReloadableDefinitionsFactory {
  public boolean refreshRequired();
}

This method will return true if the definitions sources have been modified.


Reading definitions from a data source has been externalized into its own interface. The reader interface is:

public interface DefinitionsReader {
  public void init(Map params) throws DefinitionsFactoryException;
  public Map read(Object source) throws DefinitionsFactoryException;
}

The defualt implementation is DigesterDefinitionsReader. The init method sets up digester with its parameters. The read method reads XML data from an input stream and returns a Map of definitions. It does *not* do Definitions inheritance. It simply returns a Map of definitions to the factory and leaves the inheritance up to the ComponentDefinitionsSet object graph described below:


Here is the object graph interface:

public interface ComponentDefinitionsSet {
  public void addDefinition(ComponentDefinition);
  public ComponentDefinition getDefinition(String name);
  public ComponentDefinition getDefinition(String name, Locale locale);
  public boolean isLocaleProcessed(Locale locale);
  public void addDefinitions(Map definitons);
  public void addLocaleSpecificDefinitions(
        Map definitions, Locale locale));
  public void resolveInheritances();
  public void reset(ComponentDefinitions newDefs);
}

The object contains 2 maps of definitions: a base map keyed by name, and map of locale-specific definitions. The locale-specific map is actually a map of maps of definitions. It is keyed by locale and the definitions maps are keyed by name.

I think most of the methods are self-explanatory, but here's a few notes:

* The isLocaleProcessed method returns true if a given locale has been processed. The purpose of this method is to avoid unnecessary synchronization. If a locale is given and it has already been processed but a configuration for that locale does not exist, there is no reason to lock the definitions to read the files again.

* The reset method is basically a copy constructor. It clears the existing object graph and copies in the new one. The reason I modelled it this way is for synchronization purposes. If you pull the definitions reference from ServletContext and synchronize it to update it, then change the reference to point to a new object and place that new object in ServletContext, threads that are still referencing the old object will still be using old data. But if we lock the definitions object then update its contents, anyone who is referencing that object will see the new contents.

If you made it this far, thanks :-) Please let me know what you think. I'll have code soon, I promise!!

Greg

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to