http

Project Url: wizzardo/http
More: Author   ReportBugs   
Tags:

CircleCI codecov Join the chat at https://gitter.im/wizzardo-http/Lobby

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

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
}

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"));
    });
Raw usage of DI
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

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

collect

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">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"/>
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.

each

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".

else

The logical else tag

Template:
<g:if test="${false}">
    never happen
</g:if>
<g:else>
    Hello, world!
</g:else>
Result:
    Hello, world!

elseif

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

form

Creates a form, extends 'createLink' tag


formatBoolean

Outputs the given boolean as the specified text label.

Template:
<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

hiddenField

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

if

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

join

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: ', '

layoutBody

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 decorated</body>
</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 decorated</body>
</html>

layoutHead

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 decorated</body>
</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 decorated</body>
</html>

layoutTitle

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 decorated</body>
</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 decorated</body>
</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]">link</g:link>
Result:
<a href="/book/1">link</a>
Attributes:

same as in createLink tag


message

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

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.

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

radio

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

resource

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.

set

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 = 0</p>
    <p>Current i = 1</p>
    <p>Current i = 2</p>
Attributes:
  • 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.

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.

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.

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.

while

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 = 0</p>
    <p>Current i = 1</p>
    <p>Current i = 2</p>
Attributes:
  • test - The conditional expression
Support Me
Apps
About Me
Google+: Trinea trinea
GitHub: Trinea