DarkRoast is a ultra fast and super lightweight MVC web framework built on top of Java EE 6 CDI. Inspired by Struts 2, Microsoft MVC and other easy to use frameworks, DarkRoast aims to be as simple as possible by building on standard Java libraries and established technology we all know and love.
DarkRoast provides several convenient features for CDI applications, allowing easy access to servlet objects and important life cycle events from within your application.
Inject the ServletContext
ServletRequest
and ServletResponse
into any
managed bean. DarkRoast even allows injection of the typed HttpServletRequest
and HttpServletResponse
without requiring additional casting.
@Named
public class HelloWorldService {
@Inject ServletContext servletContext;
@Inject HttpServletRequest request;
@Inject HttpServletResponse response;
public String say() {
return "Hello World!";
}
}
Observe important web application life cycle events in your application because why not.
ServletRequestEvent
- Servlet request start and stop events.ServerContextEvent
- Web application servlet context startup and shutdown.BootstrapEvent
- DarkRoast startup and shutdown events.
Qualify your observers using @Initialized
and @Destroyed
annotations to observe
start and stop events individually.
Bootstrap your own application startup after DarkRoast has done it's thing. Observe the events you need to get your application up and running with minimal effort and without cryptic DSL's and convoluted configuration.
@Startup
@ApplicationScoped
public class ApplicationBootstrap {
private static final Logger LOG = Logger.getLogger(ApplicationBootstrap.class.getName());
protected void applicationStartup(@Observes @Initialized BootstrapEvent event) {
LOG.info("Application is starting up!");
}
protected void applicationShutdown(@Observes @Destroyed BootstrapEvent event) {
LOG.info("Application is shutting down");
}
}
DarkRoast is all about the beans. Take any POJO and annotate it with a @Path
to turn it into a RequestScoped
MVC controller. Action names are inferred from the URL, or can be explicitly mapped with another @Path
.
Controller:
import com.darkroast.mvc.annotations.Path;
import com.darkroast.mvc.Controller;
import com.darkroast.mvc.results.Result;
import static com.darkroast.mvc.results.Results.*;
@Path("rythm")
public class HelloWorldController implements Controller {
@Path("index")
public Result index() {
return view("index.html").add("what", "Rythm");
}
}
index.html:
<html>
<head>
<title>Hello World!</title>
</head>
<body>
@args String what
<p>
Got @what?
</p>
</body>
</html>
Views are rendered using the Rythm Template Engine. Inspired by Microsoft's Razor and used by other frameworks like Play!, Rythm is easy to use, easy to extend and blazing fast.
View models can be passed to the view layer via the action Result
object. Any result type that renders
content will accept model parameters for rendering data back to the user. These results are also chainable, making it
simple to add many model objects to the result.
@Path("index")
public Result index() {
return view("index.html")
.add("who", "Chuck Norris")
.add("what", "a unicycle")
.add("why", "his beard");
}
Declare the @args
in your Rythm template.
@args String who
@args String what
@args String why
<p>
@who, @what and @why.
</p>
Databinding is handled through a CDI extension that dynamically produces instances of objects for injectable fields
annotated with @ViewModel
, and attempts to populate them using request parameters.
Model view beans
- A model bean must have a public default constructor
- A model bean must follow JavaBean property naming conventions
- Request parameters names must match the JavaBean property name as accessible from the Controller.
Model:
public class Person {
private String name;
public Person() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Controller:
import com.darkroast.mvc.annotations.Path;
import com.darkroast.mvc.annotations.ViewModel;
import com.darkroast.mvc.Controller;
import com.darkroast.mvc.results.Result;
import static com.darkroast.mvc.results.Results.*;
@Path("person")
public class PersonController implements Controller {
@Inject @ViewModel Person person;
@Path("index")
public Result index() {
return view("index.html").add("person", person);
}
@Path("hello")
public Result hello() {
System.out.println("Hello " + person.getName());
return view("hello.html").add("person", person);
}
}
View:
@args Person person
@if (person.getName() != null) {
Hello @person.getName()!
}
<form action="/person/hello">
<input type="text" name="person.name" value="@person.name"/>
</form>
Darkroast has serveral bundled tags for rendering standard HTML controls, links, and other common elements. These tags fully support dynamic HTML attributes, meaning you can add your CSS classes, HTML5 data attributes, placeholder values and whatever else you may need.
For example, the following Rythm Template produces markup that is remarkably similar
@a(href= "/some/url", class="myclass", data-toggle="tooltip", title="A link with a Bootstrap style tooltip!") {
<span>Hover over me</span>
}
<a href="/some/url" class="myclass" data-toggle="tooltip" title="A link with a Bootstrap style tooltip!">
<span>Hover over me</span>
</a>
Tag | Expected Attributes | Example Usage |
---|---|---|
@a | href , value or tag body |
@a("/some/url", "Link text") |
@a(href: "/some/url", value: "Link text", class: "cssclass") |
||
@a(href: "/some/url") { <span>Link text</span> } |
||
@input | name , value , optional type |
@input("person.name", "Bob Smith") |
@input(name: "person.name", value: "Bob Smith, class: "cssclass") |
||
@input(name: "person.email", value: @person.getEmail(), type: "email") |
Note that even expected parameters can be omitted and the tag should still render correctly.
Using Rythm you can easily create specific tags for your application. This is because Every template can be invoked
as a tag. Templates are resolved from the template root (/WEB-INF/content
by default), and mapped into a tag name
by stripping off the file extension and converting path separators /
into dots .
.
For example suppose you have a template with the following content located at /WEB-INF/util/hello.html
:
Hello from tag!
From the any other template or even the template itself you can invoke the template as a tag:
@util.hello()
Rythm is so fast that there is very little performance benefit to writing a Java tag that produces output with StringBuilder or String concatenation.
However, Java tags are still very useful for third party developers wanting to package tags in jar files. DarkRoast provides
a convenient mechanism for discovering and registering tags as managed beans through the use of the @RythmTag
annotation.
import com.darkroast.rythm.annotations.RythmTag;
import org.rythmengine.template.ITag;
import org.rythmengine.template.JavaTagBase;
@RythmTag
public class Hello extends JavaTagBase {
public Hello() {
}
@Override
public String __getName() {
return "hello";
}
@Override
protected void call(ITag.__ParameterList params, __Body body) {
Object o = params.getDefault();
String name = o == null ? "who" : o.toString();
p("Hello ").p(name);
}
}
<body>
<p>
@hello("Darkroast")
</p>
</body>
TODO: Write this later... Reference https://github.com/greenlaw110/play-rythm/blob/master/documentation/manual/user_guide.textile