DRAFT

This page reflects the status of the MyFaces Application Builder GSoC 2010 Project.

Work in Progress!

What works and what doesn't?

What works:

What does not work:

Improvements:

Next Steps

User Guide

Requirements

Download

The MAB resides inside a Mercurial repository. In order to download, one has to have Mercurial installed. To get the code, simply run

hg clone https://myfaces-application-builder.googlecode.com/hg/ myfaces-application-builder

After that, the source code will be in a folder called myfaces-application-builder

Compile

Enter the myfaces-application-builder directory and enter mvn install

Run

After that, enter the app subdirectory in the myfaces-application-builder directory and enter mvn exec:java -Dexec.inClass="org.apache.myfaces.gsoc.mab.Start"

Note: In order to run, the codi plugin needs a self compiled codi core in the local maven repository.

Structure

Configuration

To standardize the configuration of the MAB, a number of predefined types of configuration objects exist. The base configuration object contains a unique identifier and a message which is shown to the user (e.g. "Project name"). Furthermore, different configuration objects can have different characteristics.

Yes/No: A simple Yes/No option which can have a default setting.

Constructor for the Yes/No option:

public YesNoConfigurationObject(String key, String question) or public YesNoConfigurationObject(String key, String question, boolean defaultYes)

Text: A simple text option which can have a default text

Constructor for the simple text option:

public TextConfigurationObject(String key, String question) or public TextConfigurationObject(String key, String question, String defaultInput

Selection: A list of elements from which the user is able to choose from. Can have a default entry selected.

Constructor of a selection list:

SelectConfigurationObject(String key, String question, List<Tuple> items) or SelectConfigurationObject(String key, String question, List<Tuple> items, int defaultSelected)

=== Tuple: A key:Value pair. Can have default values. TupleConfiguration(String question) or TupleConfiguration(String question, String key, String value)

Plugins

There are two types of plugins. Plugins should implement one of the plugin interfaces.

* UI Plugins: UI plugins can be chosen by a command line argument. If none is selected or if the selection is invalid, the default shell style interface is used. The shell style interface is still very basic at the moment. UI plugins have to be able to handle the different configuration objects otherwise they won't work anyway.

* "Normal" Plugins: Are not limited. In order to work though, they have to observe some of the following events.

Events

There are several pre-defined life-cycle events. Plugins can observe these events to execute code if they need to.

* Startup: The purpose of this event is to allow plugins to check if necessary prerequisites are fulfilled (this is specifically targeting only the plugin chosen by the user)

* Generation: For generating code/configuration etc.

* Finalization: Allows plugins to execute final code (e.g. executing the app, starting a jetty, ...)

* UIEvent: Event to pass "something" to the UI (e.g. configuration related things). Received by the central class, this event is then sent to the selected UI

=== Plugin === To create a plugin, one has to implement the "Plugin" interface.

@Singleton
public class DummyPlugin implements Plugin
{

    @Inject
    private Output out;
    @Inject
    private Event<UIEvent> uiEvent;
    @Inject
    private DummyPluginConfig config;

    public void startup(
            @Observes @Startup(StartupEventType.STARTUP) @Filter(DummyPlugin.class) StartupEvent startupEvent)
    {
        config.initConfig();
        uiEvent.fire(new UIEvent(this.config));
    }

    public void generate(@Observes @Filter(DummyPlugin.class) GenerationEvent generationEvent)
    {
        //do something
        out.write(this.getClass(),
                "generate...");
        out.write(this.getClass(),
                "Your project name is: " + this.config.getAttribute("Name", TextConfiguration.class).getInput());
        out.write(this.getClass(),
                "Your project description is: " + this.config.getAttribute("Description",
                TextConfiguration.class).getInput());
        out.write(this.getClass(),
                "You selected: " + this.config.getAttribute("Select",
                SelectConfiguration.class).getSelectedContent().elementB);

        if (this.config.getAttribute("MOO", YesNoConfiguration.class).isYes())
        {
            out.write(this.getClass(), "Moo!!");
        } else
        {
            out.write(this.getClass(), "No Moo for you!");
        }
        out.write(this.getClass(), "...generated\n");
    }

    public void configure(@Observes @Filter(DummyPlugin.class) ConfigurationEvent configurationEvent)
    {
        //incoming configuration, do sth with it
        this.config = (DummyPluginConfig) configurationEvent.getConfiguration();
    }
}

To filter events for the specific plugin, the plugin should use the @Filter(Class.class). However, this is not forceable so plugins are theoretically able to observe any event.

Plugin configuration

This is the corresponding config file for the aforementioned plugin

@Singleton
public class DummyPluginConfig implements Config
{

    private LinkedHashMap<String, ConfigurationObject> configAttributes;
    private final String identifier = "mab.plugin.dummy";
    private final String name = "Dummyplugin";
    private final String description = "This is just a dummy plugin";

    /**
     * Initializes all parameters which need configuration
     */
    protected void initConfig()
    {
        setAttribute("Name", new TextConfiguration("Project name", "Test Project"));
        setAttribute("Description", new TextConfiguration("Project description", "A sample project"));
        List<Tuple> tuples = new ArrayList<Tuple>();
        tuples.add(Tuple.of("Test1", "Test1B"));
        tuples.add(Tuple.of("Test2", "Test2B"));
        tuples.add(Tuple.of("Test3", "Test3B"));
        tuples.add(Tuple.of("Test4", "Test4B"));
        setAttribute("Select", new SelectConfiguration("Selection demo", tuples));
        setAttribute("MOO", new YesNoConfiguration("Moo?", true));
    }

    private void initMap()
    {
        this.configAttributes = new LinkedHashMap<String, ConfigurationObject>();
    }

    @Override
    public boolean setAttribute(String name, ConfigurationObject value)
    {
        return setAttribute(name, value, true);
    }

    @Override
    public boolean setAttribute(String name, ConfigurationObject value, boolean forceOverride)
    {
        if (!forceOverride && containsAttribute(name))
        {
            return false;
        }
        this.getConfigAttributeMap().put(name, value);
        return true;
    }

    @Override
    public boolean containsAttribute(String name)
    {
        return this.getConfigAttributeMap().containsKey(name);
    }

    @SuppressWarnings(
    {
        "unchecked"
    })
    public <T extends ConfigurationObject> T getAttribute(String name, Class<T> targetType)
    {
        return (T) this.getConfigAttributeMap().get(name);
    }

    @Override
    public LinkedHashMap<String, ConfigurationObject> getConfigAttributeMap()
    {
        if (this.configAttributes == null)
        {
            initMap();
            this.configAttributes = new LinkedHashMap<String, ConfigurationObject>();
        }
        return this.configAttributes;
    }

    @Override
    public String getIdentifier()
    {
        return this.identifier;
    }

    @Override
    public String getDescription()
    {
        return this.description;
    }

    @Override
    public String getName()
    {
        return this.name;
    }

    @Override
    public Class getPlugin()
    {
        return DummyPlugin.class;
    }
}

To configure another plugin, just inject the plugins' config class and set some attributes carrying default values.

@Inject
private MavenPluginConfig mavenConfig;
[...]
mavenConfig.setAttribute("Destination", new TextConfiguration("Destination", "blahblah"));

IMHO, the configuration is still a bit messy. It is very simple to code a line which exceeds e.g. 150 chars.

UI Plugins

At this time, the MAB has a simple console-like interface. To create a new UI, the UIPlugin interface needs to be implemented. The two most important methods of the UIPLugin are:

start(Config co), which has to handle the passed Config object by letting the user configure the passed configuration

and

output(String output), which handles output passed by the application and the plugins.

Ideas/ToDo

Feel free to post your ideas and opinions here!