Java HTTP-server
server based on my epoll
HttpServer<HttpConnection> server = new HttpServer<>(8080);
server.getUrlMapping()
.append("/", (request, response) -> response.setBody("It's alive!"));
server.start();
Framework
- Installation
- Initialization
- Building and running
- Url-mapping
- Dependency injection
- Configuration
- Template engine
- i18n
- Taglib
Gradle
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
}
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();
}
}
./gradlew fatJar
java -jar build/libs/MyWebApp-all.jar env=prod profiles.active=profile_A,profile_B
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
;
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"));
});
Raw usage of DI
DependencyFactory.get().register(CustomBean.class, new SingletonDependency<>(CustomBean.class));
CustomBean bean = DependencyFactory.get(CustomBean.class);
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
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>Hellotitle>
head>
<body>
Hello, ${name}!
body>
html>
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"
- checkBox
- collect
- createLink
- each
- else
- elseif
- form
- formatBoolean
- hiddenField
- if
- join
- layoutBody
- layoutHead
- layoutTitle
- link
- message
- passwordField
- radio
- resource
- set
- textArea
- textField
- while
Generates a checkbox form field.
Template:
<g:checkBox name="myCheckbox" value="${true}"/>
<g:checkBox name="myCheckbox" id="myCheckbox_${1}" checked="${true}"/>
Result:
<input type="checkbox" name="myCheckbox" id="myCheckbox" value="true"/>
<input type="checkbox" name="myCheckbox" id="myCheckbox_1" checked="checked"/>
Attributes
- 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
Iterate over each element of the specified collection transforming the result using the expression in the closure
Template:
<div>
<g:collect in="${books}" expr="${it.title}">
$it<br/>
g:collect>
div>
Action:
model().append("books", Arrays.asList(new Book("Book one"), new Book("Book two")))
Book.java:
public class Book {
public final String title;
public Book(String title) {
this.title = title;
}
}
Result:
<div>
Book one
<br/>
Book two
<br/>
div>
Attributes
- in - The object to iterative over
- expr - A expression
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");
Template:
// generates <a href="/book/1">linka>
<a href="${createLink([controller:'book', action:'show', id: 1])}">linka>
// 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"/>
Attributes:
- 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.
Iterate over each element of the specified collection.
Template:
<div>
<g:each in="[1,2,3]" var="i">
$i<br/>
g:each>
div>
Result:
<div>
1
<br/>
2
<br/>
3
<br/>
div>
Attributes:
- 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".
The logical else tag
Template:
<g:if test="${false}">
never happen
g:if>
<g:else>
Hello, world!
g:else>
Result:
Hello, world!
The logical elseif tag
Template:
<g:if test="${false}">
never happen
g:if>
<g:elseif test="${true}">
Hello, world!
g:elseif>
Result:
Hello, world!
Attributes:
- test - The expression to test
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!" />
Result:
true
True!
Attributes:
- 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
Creates a input of type 'hidden' (a hidden field).
Template:
<g:hiddenField name="myField" value="myValue" />
Result:
<input type="hidden" name="myField" id="myField" value="myValue"/>
Attributes:
- name - The name of the text field
- value (optional) - The value of the text field
The logical if tag to switch on an expression.
Template:
<g:if test="${true}">
Hello!
g:if>
Result:
Hello!
Attributes:
- test - The expression to test
Concatenate the toString() representation of each item in this collection with the given separator.
Template:
<g:join in="['Hello', 'World!']" delimiter=", "/>
Result:
Hello, World!
Attributes:
- in - The collection to iterate over
- delimiter (optional) - The value of the delimiter to use during the join. Default: ', '
Used in layouts to output the contents of the body tag of the decorated page.
Decorated page template:
<html>
<head>
<meta name="layout" content="myLayout" />
<script src="myscript.js" />
head>
<body>Page to be decoratedbody>
html>
Layout template views/layouts/myLayout.gsp
:
<html>
<head>
<script src="global.js" />
<g:layoutHead />
head>
<body><g:layoutBody />body>
html>
Result:
<html>
<head>
<script src="global.js" />
<script src="myscript.js" />
head>
<body>Page to be decoratedbody>
html>
Used in layouts to render the contents of the head tag of the decorated page
Decorated page template:
<html>
<head>
<meta name="layout" content="myLayout" />
<script src="myscript.js" />
head>
<body>Page to be decoratedbody>
html>
Layout template views/layouts/myLayout.gsp
:
<html>
<head>
<script src="global.js" />
<g:layoutHead />
head>
<body><g:layoutBody />body>
html>
Result:
<html>
<head>
<script src="global.js" />
<script src="myscript.js" />
head>
<body>Page to be decoratedbody>
html>
Used in layouts to render the contents of the title tag of the decorated page
Decorated page template:
<html>
<head>
<meta name="layout" content="myLayout" />
<title>Hello World!title>
<script src="myscript.js" />
head>
<body>Page to be decoratedbody>
html>
Layout template views/layouts/myLayout.gsp
:
<html>
<head>
<title><g:layoutTitle default="Some Title" />title>
<script src="global.js" />
<g:layoutHead />
head>
<body><g:layoutBody />body>
html>
Result:
<html>
<head>
<title>Hello World!title>
<script src="global.js" />
<script src="myscript.js" />
head>
<body>Page to be decoratedbody>
html>
Creates an html anchor tag with the href set based on the specified parameters. Extends 'createLink' tag.
Template:
<g:link controller="book" action="show" params="[id: 1]">linkg:link>
Result:
<a href="/book/1">linka>
Attributes:
Resolves a message from the given code.
Template:
// test.message.1.args = test message: {0}
<g:message code="test.message.${1}.args" args="['one']"/>
Result:
test message: one
Attributes:
- 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
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.
Template:
<g:passwordField name="myPasswordField" value="${'myPassword'}" />
Result:
<input type="password" name="myPasswordField" id="myPasswordField" value="myPassword" />
Attributes:
- name - The name of the password field
- value - The value of the password field
Generates a radio button
Template:
<g:radio name="myGroup" value="1"/>
<g:radio name="myGroup" value="2" checked="true"/>
Result:
<input type="radio" name="myGroup" value="1" />
<input type="radio" name="myGroup" checked="checked" value="2" />
Attributes:
- 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
Generates a link (URI) string for static resources. Can be used in an href, JavaScript, Ajax call, etc.
Template:
<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"/>
Result:
<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>
Attributes:
- 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.
Sets the value of a variable accessible with the GSP page.
Template:
<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>
Result:
<p>Current i = 0p>
<p>Current i = 1p>
<p>Current i = 2p>
Attributes:
- var - The name of the variable
- value - The initial value
Creates a HTML text area element. An implicit "id" attribute is given the same value as name unless you explicitly specify one.
Template:
<g:textArea name="myField" value="myValue" rows="5" cols="40"/>
Result:
<textarea name="myField" id="myField" rows="5" cols="40" >
myValue
textarea>>
Attributes:
- 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.
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.
Template:
<g:textField name="myField" value="${'input'}" />
Result:
<input type="text" name="myField" id="myField" value="input" />
Attributes:
- 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.
Executes a condition in a loop until the condition returns false.
Template:
<g:while test="${i < 3}">
<p>Current i = ${i++}p>
g:while>
Result:
<p>Current i = 0p>
<p>Current i = 1p>
<p>Current i = 2p>
Attributes:
- test - The conditional expression