JFry

Java Fry is a web library inspired by Sinatra

License

License

GroupId

GroupId

com.github.ggalmazor
ArtifactId

ArtifactId

core
Last Version

Last Version

0.0.15
Release Date

Release Date

Type

Type

jar
Description

Description

JFry
Java Fry is a web library inspired by Sinatra

Download core

How to add to project

<!-- https://jarcasting.com/artifacts/com.github.ggalmazor/core/ -->
<dependency>
    <groupId>com.github.ggalmazor</groupId>
    <artifactId>core</artifactId>
    <version>0.0.15</version>
</dependency>
// https://jarcasting.com/artifacts/com.github.ggalmazor/core/
implementation 'com.github.ggalmazor:core:0.0.15'
// https://jarcasting.com/artifacts/com.github.ggalmazor/core/
implementation ("com.github.ggalmazor:core:0.0.15")
'com.github.ggalmazor:core:jar:0.0.15'
<dependency org="com.github.ggalmazor" name="core" rev="0.0.15">
  <artifact name="core" type="jar" />
</dependency>
@Grapes(
@Grab(group='com.github.ggalmazor', module='core', version='0.0.15')
)
libraryDependencies += "com.github.ggalmazor" % "core" % "0.0.15"
[com.github.ggalmazor/core "0.0.15"]

Dependencies

compile (6)

Group / Artifact Type Version
org.eclipse.jetty : jetty-servlets jar 9.3.2.v20150730
commons-io : commons-io jar 2.4
org.eclipse.jetty : jetty-server jar 9.3.2.v20150730
org.eclipse.jetty : jetty-servlet jar 9.3.2.v20150730
ch.qos.logback : logback-classic jar 1.1.3
com.javaslang : javaslang jar 2.0.0-beta

test (2)

Group / Artifact Type Version
junit : junit jar 4.12
org.assertj : assertj-core jar 3.1.0

Project Modules

There are no modules declared in this project.

JFry Build Status Gitter codecov.io

Java Fry is a web library inspired by Sinatra

Getting started

This will improve with time. You just create a new instance with a server adapter and a port number.

Then you can use JFry's DSL to register new Routes with their Handlers:

JFry.of(new JettyAdapter(), 8080)
  .options("/foo", req -> req.buildResponse().ok("bar"))
  .get("/foo", req -> req.buildResponse().ok("bar"))
  .head("/foo", req -> req.buildResponse().ok("bar"))
  .post("/foo", req -> req.buildResponse().ok("bar"))
  .put("/foo", req -> req.buildResponse().ok("bar"))
  .delete("/foo", req -> req.buildResponse().ok("bar"))
  .trace("/foo", req -> req.buildResponse().ok("bar"))
  .connect("/foo", req -> req.buildResponse().ok("bar"))
  .start();

Now you can browse to http://localhost:8080/foo

Why is JFry different?

JFry is strongly based on concepts related to Functional Programming: immutable objects, first-class functions, etc.

Probably the main difference between JFry and almost any other Java web library is that Route Handlers are defined as Request -> Response functions. This is a key design decision in order to benefit from functions' composition and decoration capabilities.

JFry doesn't make any assumption on the kind of use that you're going to give it. Therefore, the core artifact doesn't provide any templating engine, authentication method, json transformer, nothing, nada. Instead, it gives you an easy way to implement your own decorators to handle template rendering, input/output deserialization/serialization, authentication, session handling, etc.

Decorators and function chains

A function decorator is a function that extends the functionality of other compatible functions. JFry defines for your convenience some Functional Interfaces to make easier for you the creation of decorators Requests and Responses.

If you are using a simple Request -> Response Handler like so:

jfry.get("/doge", request -> request.buildResponse.ok("wow"))

And you need to allow access only to authenticated users, you could do something like:

// Provided that you have some authentication object like this one:

class Authenticator {
  public Handler authenticate(Handler handler) {
    return request -> {
      return request.mapHeader("X-Auth-Token", JWT::validate).orElse(false) 
               ? handler.apply(request) 
               : request.buildResponse().unathorized();
    }
  }
}

// Then you can do:

jfry.get("/doge", authenticator.authenticate(request -> request.buildResponse.ok("wow"))));

This mode of operation is based on a decorator that returns a new function that complies with a Handler Functional Interface and can be used as a Handler itself. This way, your initial Handler doesn't have to know anything about authentication and can be "clean" of dependencies and lines of code that don't have anything to do with your real business logic.

Sometimes, it's enough to manipulate the Request or the Response method. This is very useful when you need to deal with json content. Let's say that you need to receive a json object from your ursers and then return some other json to them. You could do:

// Provided that you have some json serialization/deserialization engine like this:

class JsonEngine {
  public RequestDecorator deserialize() {
    return request -> request.mapBody(Json::deserialize)
                             .map(request::withBody)
                             .orElse(request);
  }
  
  public ResponseDecorator serialize() {
    return response -> response.mapBody(Json::serialize)
                               .map(response::withBody)
                               .orElse(response);
  }
}

// Then you can do:

jfry.get("/doge", json.deserialize()
                      .andThen(request -> request.buildResponse.ok(request.<Doge>getBody()))
                      .andThen(json.serialize()));

RequestDecorator and ResponseDecorator are Functiontal Interfaces declared in com.github.ggalmazor.jfry.decorators package.

In this case, we're taking advantage of the implicit type casts that occur inside Request and Response objects when you call to mapBody() or getBody(). You need to remember that this operations are unsafe. JFry will always think that you know what you're doing :)

Use the tests

You can learn a lot about JFry browsing through its tests

Versions

Version
0.0.15