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:
- console ui
- create maven's pom.xml
- create cdi's beans.xml
create web.xml and faces-config.xml for MyFaces
- create trinidad blank example project + run in jetty
- create codi example project
What does not work:
- faces-config.xml, web.xml and pom.xml are missing some configuration options
- create a custom application w/ choice of components etc.
- codi + jetty seems to be a problem (hint: it works with jetty 7.1.5.v20100705)
- run standalone as executeable jar
Improvements:
- configuration of other plugins needs improvments
- handling of configuration in the ui
Next Steps
- Introducing a fine grained module structure (maven modules)
Refactoring the plugin system (avoid dependencies between plugins and plugins like the Trinidad plugin shouldn't be aware of the ui - see InputStreamReader)
User Guide
Requirements
- Mercurial
- Maven
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
- improve configuration of other plugins
- eliminate inter-plugin dependencies
- ...
Feel free to post your ideas and opinions here!