server based on my epoll
HttpServer<HttpConnection> server = new HttpServer<>(8080);
server.getUrlMapping()
.append("/", (request, response) -> response.setBody("It's alive!"));
server.start();
- Installation
- Initialization
- Building and running
- Url-mapping
- Dependency injection
- Configuration
- Template engine
- i18n
- Taglib
Installation ↑
build.gradle
apply plugin: 'java'
apply plugin: 'application'
repositories {
jcenter()
mavenCentral()
maven {
url "https://oss.sonatype.org/content/repositories/snapshots/"
}
}
sourceCompatibility = 1.8
targetCompatibility = 1.8
version = '0.1'
mainClassName = "com.example.MyWebApp"
dependencies {
compile 'com.wizzardo:http:0.2+'
}
//create a single Jar with all dependencies
task fatJar(type: Jar) {
manifest {
attributes(
"Main-Class": mainClassName
)
}
baseName = project.name + '-all'
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
with jar
}
Initialization ↑
import com.wizzardo.http.framework.Controller;
import com.wizzardo.http.framework.Environment;
import com.wizzardo.http.framework.WebApplication;
import com.wizzardo.http.framework.template.Renderer;
public class MyWebApp {
static class AppController extends Controller {
public Renderer index() {
return renderString("It's alive!");
}
}
public static void main(String[] args) {
WebApplication application = new WebApplication(args);
application.onSetup(app -> {
app.getUrlMapping()
.append("/", AppController.class, "index");
});
application.start();
}
}
Building and running ↑
./gradlew fatJar
java -jar build/libs/MyWebApp-all.jar env=prod profiles.active=profile_A,profile_B
Url-mapping ↑
Controllers and actions could be mapped to static paths or to something dynamic with variables and wildcards
urlMapping
.append("/index", AppController.class, "index")
.append("/books/$id?", AppController.class, "books") // 'id' - is optional
.append("/optionals/$foo?/$bar?", AppController.class, "optionals") // 'foo' and 'bar' - are optional
.append("/${foo}-${bar}", AppController.class, "fooBar")
.append("/any/*", AppController.class, "any")
.append("*.html", AppController.class, "html")
.append("/upload", AppController.class, "upload", Request.Method.POST) // only POST method is allowed
;
Dependency injection ↑
Framework supports simple dependency injections, to make class or interface injectable simple annotate it with @Injectable.
There are several scopes for it:
- SINGLETON - one instance per jvm, default
- PROTOTYPE - new instance for every injection
- SESSION - one instance per user-session
- REQUEST - new instance every request
- THREAD_LOCAL - one instance per thread
Controllers are stateful so their scope is PROTOTYPE, Services - SINGLETON.
static class AppController extends Controller {
AppService appService;
public Renderer index() {
return renderString(appService.getMessage());
}
}
To make dependency injection work with implicit dependencies, you need to specify package for scan:
application.onSetup(app -> {
DependencyFactory.get(ResourceTools.class)
.addClasspathFilter(className -> className.startsWith("com.example"));
});
DependencyFactory.get().register(CustomBean.class, new SingletonDependency<>(CustomBean.class));
CustomBean bean = DependencyFactory.get(CustomBean.class);
Configuration ↑
src/main/resources/Config.groovy
server {
host = '0.0.0.0'
port = 8080
ioWorkersCount = 1
ttl = 5 * 60 * 1000
context = 'myApp'
basicAuth {
username = 'user'
password = 'pass'
token = true
tokenTTL = 7 * 24 * 60 * 60 * 1000l
tokenized { // creates mapping with path '/resourceName/*' and respective name for static resources available by token
resourceName = 'path/to/resource'
}
}
ssl {
cert = '/etc/ssl/certs/hostname.crt'
key = '/etc/ssl/private/hostname.key'
}
session {
ttl = 30 * 60
}
resources {
path = 'public' // load static files from resource folder 'public'
mapping = '/static'
cache = {
enabled = true
gzip = true
ttl = -1L
memoryLimit = 32 * 1024 * 1024L
maxFileSize = 5 * 1024 * 1024L
}
}
debugOutput = false // dump of all requests and responses to System.out
}
//this configuration will be only applied for certain environment
environments {
dev {
custom.key = true
}
prod {
custom.key = false
server.ioWorkersCount = 4
}
}
//this configuration will be only applied for certain profiles
profiles {
profile_A {
environments {
dev {
value = 'value_dev_A'
}
prod {
value = 'value_prod_A'
}
}
}
profile_B {
value = 'value_B'
}
}
Configuration stored in Holders:
boolean key = Holders.getConfig().config("custom").get("key", defaulValue);
or it can be mapped to pojo and injected to other objects:
public class CustomConfig implements Configuration {
public boolean key;
@Override
public String prefix() {
return "custom";
}
}
Configuration is loaded in this order:
- Default configuration and manifest
- Config.groovy
- External configuration (
webApp.onLoadConfiguration(app -> app.loadConfig("MyCustomConfig.groovy"))
) - Profiles and environments
- OS environment variables (
System.getenv()
) - Java System properties (
System.getProperties()
) - Command line arguments
Template engine ↑
This framework has it's own template engine, inspired and based on Groovy Server Pages (GSP)
static class AppController extends Controller {
public Renderer index() {
model().append("name", params().get("name", "%user name%"));
return renderView();
}
}
Engine will try to render html from template 'resources/views/controller_name/view_name.gsp', by default 'view_name' is 'action_name':
src/main/resources/views/app/index.gsp
<html>
<head>
<title>Hello</title>
</head>
<body>
Hello, ${name}!
</body>
</html>
i18n ↑
MessageBundle ms = DependencyFactory.get(MessageBundle.class);
//load message bundle from resources/i18n/messages.properties
//and lazy load any other language, for example messages_en.properties, messages_fr.properties
ms.load("messages");
String foo = ms.get("foo");
String fooDe = ms.get(Locale.GERMANY,"foo");
//it also supports templates
//foobar = {0} {1}
String foobar = ms.get("foobar", "foo", "bar"); // "foo bar"
Taglib ↑
- checkBox
- collect
- createLink
- each
- else
- elseif
- form
- formatBoolean
- hiddenField
- if
- join
- layoutBody
- layoutHead
- layoutTitle
- link
- message
- passwordField
- radio
- resource
- set
- textArea
- textField
- while
checkBox ↑
Generates a checkbox form field.
<g:checkBox name="myCheckbox" value="${true}"/>
<g:checkBox name="myCheckbox" id="myCheckbox_${1}" checked="${true}"/>
<input type="checkbox" name="myCheckbox" id="myCheckbox" value="true"/>
<input type="checkbox" name="myCheckbox" id="myCheckbox_1" checked="checked"/>
- name - The name of the checkbox
- value (optional) - The value of the checkbox
- checked (optional) - Expression if evaluates to true sets to checkbox to checked
collect ↑
Iterate over each element of the specified collection transforming the result using the expression in the closure
<div>
<g:collect in="${books}" expr="${it.title}">
$it<br/>
</g:collect>
</div>
model().append("books", Arrays.asList(new Book("Book one"), new Book("Book two")))
public class Book {
public final String title;
public Book(String title) {
this.title = title;
}
}
<div>
Book one
<br/>
Book two
<br/>
</div>
- in - The object to iterative over
- expr - A expression
createLink ↑
Creates a link that can be used where necessary (for example in an href, JavaScript, Ajax call etc.)
Controller:
public class BookController extends Controller {
public Renderer list() {
return renderString("list of books");
}
public Renderer show() {
return renderString("some book");
}
}
url-mapping:
app.getUrlMapping()
.append("/book/list", BookController.class, "list")
.append("/book/$id", BookController.class, "show");
// generates <a href="/book/1">link</a>
<a href="${createLink([controller:'book', action:'show', id: 1])}">link</a>
// generates "/book/show?foo=bar&boo=far"
<g:createLink controller="book" action="show" params="[foo: 'bar', boo: 'far']"/>
// generates "/book/list"
<g:createLink controller="book" action="list" />
// generates "http://localhost:8080/book/list"
<g:createLink controller="book" action="list" absolute="true"/>
// generates "http://ya.ru/book/list"
<g:createLink controller="book" action="list" base="http://ya.ru"/>
- action (optional) - The name of the action to use in the link; if not specified the current action will be linked
- controller (optional) - The name of the controller to use in the link; if not specified the current controller will be linked
- id (optional) - The id to use in the link
- fragment (optional) - The link fragment (often called anchor tag) to use
- mapping (optional) - The named URL mapping to use, default mapping = controllerName + '.' + actionName
- params (optional) - A Map of request parameters
- absolute (optional) - If true will prefix the link target address with the value of the server.host property from config
- base (optional) - Sets the prefix to be added to the link target address, typically an absolute server URL. This overrides the behaviour of the absolute property if both are specified.
each ↑
Iterate over each element of the specified collection.
<div>
<g:each in="[1,2,3]" var="i">
$i<br/>
</g:each>
</div>
<div>
1
<br/>
2
<br/>
3
<br/>
</div>
- in - The collection to iterate over
- status (optional) - The name of a variable to store the iteration index in. Starts with 0 and increments for each iteration.
- var (optional) - The name of the item, defaults to "it".
else ↑
The logical else tag
<g:if test="${false}">
never happen
</g:if>
<g:else>
Hello, world!
</g:else>
Hello, world!
elseif ↑
The logical elseif tag
<g:if test="${false}">
never happen
</g:if>
<g:elseif test="${true}">
Hello, world!
</g:elseif>
Hello, world!
- test - The expression to test
form ↑
Creates a form, extends 'createLink' tag
##### formatBoolean [↑](#taglib) Outputs the given boolean as the specified text label.
<g:formatBoolean boolean="${myBoolean}" />
<g:formatBoolean boolean="${myBoolean}" true="True!" false="False!" />
true
True!
- boolean - Variable to evaluate
- true (optional) - Output if value is true. If not specified, 'boolean.true' or 'default.boolean.true' is looked up from the Message Source
- false (optional) - Output if value is false. If not specified, 'boolean.false' or 'default.boolean.false' is looked up from the Message Source
hiddenField ↑
Creates a input of type 'hidden' (a hidden field).
<g:hiddenField name="myField" value="myValue" />
<input type="hidden" name="myField" id="myField" value="myValue"/>
- name - The name of the text field
- value (optional) - The value of the text field
if ↑
The logical if tag to switch on an expression.
<g:if test="${true}">
Hello!
</g:if>
Hello!
- test - The expression to test
join ↑
Concatenate the toString() representation of each item in this collection with the given separator.
<g:join in="['Hello', 'World!']" delimiter=", "/>
Hello, World!
- in - The collection to iterate over
- delimiter (optional) - The value of the delimiter to use during the join. Default: ', '
layoutBody ↑
Used in layouts to output the contents of the body tag of the decorated page.
<html>
<head>
<meta name="layout" content="myLayout" />
<script src="myscript.js" />
</head>
<body>Page to be decorated</body>
</html>
<html>
<head>
<script src="global.js" />
<g:layoutHead />
</head>
<body><g:layoutBody /></body>
</html>
<html>
<head>
<script src="global.js" />
<script src="myscript.js" />
</head>
<body>Page to be decorated</body>
</html>
layoutHead ↑
Used in layouts to render the contents of the head tag of the decorated page
<html>
<head>
<meta name="layout" content="myLayout" />
<script src="myscript.js" />
</head>
<body>Page to be decorated</body>
</html>
<html>
<head>
<script src="global.js" />
<g:layoutHead />
</head>
<body><g:layoutBody /></body>
</html>
<html>
<head>
<script src="global.js" />
<script src="myscript.js" />
</head>
<body>Page to be decorated</body>
</html>
layoutTitle ↑
Used in layouts to render the contents of the title tag of the decorated page
<html>
<head>
<meta name="layout" content="myLayout" />
<title>Hello World!</title>
<script src="myscript.js" />
</head>
<body>Page to be decorated</body>
</html>
<html>
<head>
<title><g:layoutTitle default="Some Title" /></title>
<script src="global.js" />
<g:layoutHead />
</head>
<body><g:layoutBody /></body>
</html>
<html>
<head>
<title>Hello World!</title>
<script src="global.js" />
<script src="myscript.js" />
</head>
<body>Page to be decorated</body>
</html>
link ↑
Creates an html anchor tag with the href set based on the specified parameters. Extends 'createLink' tag.
<g:link controller="book" action="show" params="[id: 1]">link</g:link>
<a href="/book/1">link</a>
message ↑
Resolves a message from the given code.
// test.message.1.args = test message: {0}
<g:message code="test.message.${1}.args" args="['one']"/>
test message: one
- code - The code to resolve the message for.
- default (optional) - The default message to output if the error or code cannot be found in messages.properties.
- args (optional) - A list of argument values to apply to the message when code is used.
- locale (optional) Override Locale to use instead of the one detected
passwordField ↑
Creates a input of type 'password' (a password field). An implicit "id" attribute is given the same value as name unless you explicitly specify one.
<g:passwordField name="myPasswordField" value="${'myPassword'}" />
<input type="password" name="myPasswordField" id="myPasswordField" value="myPassword" />
- name - The name of the password field
- value - The value of the password field
radio ↑
Generates a radio button
<g:radio name="myGroup" value="1"/>
<g:radio name="myGroup" value="2" checked="true"/>
<input type="radio" name="myGroup" value="1" />
<input type="radio" name="myGroup" checked="checked" value="2" />
- value - The value of the radio button
- name - The name of the radio button
- checked - boolean to indicate that the radio button should be checked
resource ↑
Generates a link (URI) string for static resources. Can be used in an href, JavaScript, Ajax call, etc.
<g:resource dir="css" file="main.css" />
<g:resource dir="css" file="main.css" absolute="true" />
<g:resource dir="js" file="test.js"/>
<img src="${resource(dir:'img', file:'logo.jpg')}">
<g:resource dir="js" file="test.tag" tag="script" url="src" type="riot/tag"/>
<link rel="stylesheet" href="/static/css/main.vEA6A.css">
<link rel="stylesheet" href="http://localhost:8080/static/css/main.vEA6A.css">
<script type="text/javascript" src="/static/js/test.v207A.js"></script>
<img src="/static/img/logo.v53B0.jpg"/>
<script type="riot/tag" src="/static/js/test.v73CF.tag"></script>
- dir - the name of the directory containing the resource
- file - the name of the resource file
- tag - the name of the html tag
- url - the name of the attribute for the url
- absolute - If true will prefix the link target address with the value of the
host
property from Config.groovy.
set ↑
Sets the value of a variable accessible with the GSP page.
<g:set var="counter" value="${0}" />
<g:while test="${counter < 3}">
<p>Current i = ${i}</p>
<g:set var="counter" value="${counter + 1}" />
</g:while>
<p>Current i = 0</p>
<p>Current i = 1</p>
<p>Current i = 2</p>
- var - The name of the variable
- value - The initial value
textArea ↑
Creates a HTML text area element. An implicit "id" attribute is given the same value as name unless you explicitly specify one.
<g:textArea name="myField" value="myValue" rows="5" cols="40"/>
<textarea name="myField" id="myField" rows="5" cols="40" >
myValue
</textarea>>
- name - The name of the text area
- value - The initial text to display in the text area. By default the text area will be empty.
textField ↑
Creates a input of type 'text' (a text field). An implicit "id" attribute is given the same value as the name unless you explicitly specify one.
<g:textField name="myField" value="${'input'}" />
<input type="text" name="myField" id="myField" value="input" />
- name - The name of the text field
- value - The initial text to display in the text field. By default the text field will be empty.
while ↑
Executes a condition in a loop until the condition returns false.
<g:while test="${i < 3}">
<p>Current i = ${i++}</p>
</g:while>
<p>Current i = 0</p>
<p>Current i = 1</p>
<p>Current i = 2</p>
- test - The conditional expression