2
0
Fork 0
mirror of https://github.com/ethauvin/rife2.git synced 2025-05-01 02:58:12 -07:00
Full-stack, no-declaration, framework to quickly and effortlessly create web applications with modern Java. https://github.com/rife2/rife2
Find a file
2022-12-28 19:01:57 -05:00
app Added an example of using continuations to implement a counter. 2022-12-27 22:16:21 -05:00
gradle/wrapper Initial commit 2022-10-01 18:42:06 -04:00
lib Minor cleanup 2022-12-28 19:01:57 -05:00
standalone Added an example of using continuations to implement a counter. 2022-12-27 22:16:21 -05:00
war Reworked examples to have a global HelloAll example that includes all the example sites. 2022-12-26 17:52:47 -05:00
.gitattributes Initial commit 2022-10-01 18:42:06 -04:00
.gitignore Updated .gitignore 2022-12-28 00:35:34 -05:00
gradlew Initial commit 2022-10-01 18:42:06 -04:00
gradlew.bat Initial commit 2022-10-01 18:42:06 -04:00
LICENSE.txt Added informational documents 2022-10-04 21:51:45 -04:00
README.md Updated version to 0.9.2 2022-12-28 12:42:09 -05:00
rife2_logo.png Added a README 2022-10-30 20:42:35 -04:00
settings.gradle.kts Added HelloContinuations example. 2022-12-26 15:48:26 -05:00

Welcome

RIFE2 is a full-stack, no-declaration, framework to quickly and effortlessly create web applications with modern Java.

RIFE2 is built on the foundations of the original RIFE framework that was popular from 2002-2010. Since then, the world and Java have changed and many of the original RIFE APIs can finally be replaced with pure Java, no-XML, no-YAML, leaving only type-safe expressive code.

This is a quick tutorial, the full documentation contains a lot more information.

NOTE: The documentation and this readme are work-in-progress

Hello World Example

This is how you get started with a Hello World site.

public class HelloWorld extends Site {
    public void setup() {
        get("/hello", c -> c.print("Hello World"));
    }

    public static void main(String[] args) {
        new Server().start(new HelloWorld());
    }
}

The main method spins up the integrated embedded Jetty server, so that you can immediately start coding. The same HelloWorld class can be added as a parameter value to your web.xml, requiring absolute no changes to your code between development and production.

Out-of-container testing is a first-class citizen in RIFE2, directly interacting with your Site class to simulate full request-response interactions, without having to spin up a servlet container.

Let's test the example above with JUnit 5:

class HelloTest {
    @Test void verifyHelloWorld() {
        var m = new MockConversation(new HelloWorld());
        assertEquals("Hello World", m.doRequest("/hello").getText());
    }
}

Here's an example snippet that should help you compile and run this example with Gradle. Please make sure to adapt the artifact versions to the latest ones.

application {
    mainClass.set("HelloWorld")
}

dependencies {
    implementation("com.uwyn.rife2:rife2:0.9.1")
    testImplementation("org.junit.jupiter:junit-jupiter:5.8.2")
    testImplementation("org.jsoup:jsoup:1.15.3")
    runtimeOnly("org.eclipse.jetty:jetty-server:11.0.12")
    runtimeOnly("org.eclipse.jetty:jetty-servlet:11.0.12")
}

RIFE2 doesn't publish dependencies for jsoup nor Jetty because neither of them should be packaged with a production deployment.

You will want to:

  • depend on org.jsoup:jsoup if you want to parse HTML pages in RIFE2's web testing API
  • depend on org.eclipse.jetty if you're launching the embedded server like in these examples

RIFE2 also ships with example Gradle projects that should help you get set up quickly.

Please take a look here:

Once you've got everything set up, give it a try and visit http://localhost:8080/hello

One of the most brittle aspects of web application development is typing links and URLs as text literals, without anything guaranteeing they remain correct when your routes change or when you deploy your application in different web application contexts. RIFE2's routing API allows all your application links to be generated correctly without any effort on your behalf.

Let's add a new route that contains an HTML link towards the previous Hello World route.

You can see that routes don't have to be created inside the setup() method, but can also be created as part of your Site's construction, allowing the routes to be stored in fields.

public class HelloLink extends Site {
    Route hello = get("/hello", c -> c.print("Hello World"));
    Route link = get("/link", c-> c.print("<a href='" + c.urlFor(hello) + "'>Hello</a>"));

    public static void main(String[] args) {
        new Server().start(new HelloLink());
    }
}

We can now test this as such:

class HelloTest {
    @Test void verifyHelloLink() {
        var m = new MockConversation(new HelloLink());
        assertEquals("Hello World", m.doRequest("/link")
            .getParsedHtml().getLinkWithText("Hello")
            .follow().getText());
    }
}

Bidirectional Logic-Less Templates

The main impetus that had me resume work on RIFE2, was the unique template engine.

RIFE2's templates contain two main concepts:

  • values - that can be filled in with content and data
  • blocks - that will be stripped away and that provide content snippets

Your Java code will compose the final layout by assigning and appending blocks, and by putting data into values. Let's rewrite the HelloLink example above with a template.

In this example, no template manipulation is done in Java yet.

Instead, it introduces the {{v route:hello/}} value tag, which will automatically be replaced with the URL of the route that is available with that field name in your active Site.

public class HelloTemplate extends Site {
    Route hello = get("/hello", c -> c.print("Hello World"));
    Route link = get("/link", c-> c.print(c.template("HelloTemplate")));

    public static void main(String[] args) {
        new Server().start(new HelloTemplate());
    }
}

With HelloTemplate.html being:

<!DOCTYPE html>
<html lang="en">
<body>
<a href="{{v route:hello/}}">Hello</a>
</body>
</html>

Note that RIFE2 internally transforms your templates into Java classes by generating heavily optimized bytecode.

This happens on-the-fly during development. For production, templates can be pre-compiled, making them incredibly fast.

Template Manipulation

Let's change the example some more and create a single route that can respond to both get and post requests.

  • the get request will display a form with a single button to click.
  • the post request will receive the form's submission and display Hello World.
public class HelloForm extends Site {
    Route hello = route("/hello", c -> {
        var t = c.template("HelloForm");
        switch (c.method()) {
            case GET -> t.setBlock("content", "form");
            case POST -> t.setBlock("content", "text");
        }
        c.print(t);
    });

    public static void main(String[] args) {
        new Server().start(new HelloForm());
    }
}

With HelloForm.html being:

<!DOCTYPE html>
<html lang="en">
<body>
<!--v content/-->
<!--b form-->
<form action="{{v route:hello/}}" method="post" name="hello">
  <input type="submit" name="Submit">
</form>
<!--/b-->
<!--b text--><p id="greeting">Hello World</p><!--/b-->
</body>
</html>

You can see that the template contains all the pieces to create both pages:

  • the value named content
  • the block named form
  • the block named text

In Java, we simply assign either block to the value, depending on what we want to display.

Another benefit is that RIFE2's template tags can be HTML comments, making them completely invisible. This allows you to work on your HTML design as usual and preview the template file with a regular browser.

Finally, let's include a test for this functionality:

class HelloTest {
    @Test void verifyHelloForm() {
        var m = new MockConversation(new HelloForm());
        var r = m.doRequest("/hello").getParsedHtml()
            .getFormWithName("hello").submit();
        assertEquals("Hello World", r.getParsedHtml()
            .getDocument().body()
            .getElementById("greeting").text());
    }
}

Read more in the full documentation.