All Downloads are FREE. Search and download functionalities are using the official Maven repository.

templates.demos.helloWorld.html Maven / Gradle / Ivy

The newest version!
{#==========================================
Demos - Hello World
==========================================#}
{% extends "../demos.html" %}

{% block subSectionClasses %}demo_hello_world{% endblock %}
{% block meta_title %}Demo - Hello World!{% endblock %}
{% block meta_description %}"Hello World!" demo and tutorial using Spincast{% endblock %}

{% block subBody %}

"Hello World!" tutorial

{#========================================== Hello World (quick version) ==========================================#}

In this tutorial, we're going to present two "Hello World!" versions:

  • A quick and dirty version: This example will be very simple and its only purpose will be to show how a Spincast application can be quickly bootstrapped.
  • A more scalable version: In that one, we will use a better structure for our application and we will introduce some core Spincast features.

1. A quick and dirty version

First, you add the latest version of the org.spincast:spincast-default Maven artifact to your pom.xml (or build.gradle):

<dependency>
    <groupId>org.spincast</groupId>
    <artifactId>spincast-default</artifactId>
    <version>{{spincastCurrrentVersion}}</version>
</dependency>

Then you bootstrap everything using a main(...) method :

public class App {

    public static void main(String[] args) {

        Injector guice = Guice.createInjector(new SpincastDefaultGuiceModule(args));

        IDefaultRouter router = guice.getInstance(IDefaultRouter.class);
        
        router.GET("/").save(context -> context.response().sendPlainText("Hello World!"));

        guice.getInstance(IServer.class).start();
    }
}

Explanation :
(Tip : try hovering/clicking the page numbers below!)

  • 3 : A good old Java's main(...) method! This is the entry point of a standard Spincast application.
  • 5 : The very first thing a Spincast application does is to create a Guice context. Here, we use the provided SpincastDefaultGuiceModule module. This module binds a default implementation for all the required components of a Spincast application.
  • 7 : We get the default router (IDefaultRouter) from Guice.
  • 9 : We add a GET route for the "/" index page, using a Java 8's lambdas syntax. This inline route handler sends a plain text "Hello World!" message to the user.
  • 11 : We get the HTTP server (IServer) from Guice and we start it!

Now, if you would point your browser to http://localhost:44419 (Spincast default port is 44419), you should see the "Hello World!" message.

Here's the exact same example but using Java 7, so without lambdas. We prefer this version for a first example because the types of the various components are more obvious!:

public class App {

    public static void main(String[] args) {

        Injector guice = Guice.createInjector(new SpincastDefaultGuiceModule(args));

        IDefaultRouter router = guice.getInstance(IDefaultRouter.class);

        router.GET("/").save(new IDefaultHandler() {
            @Override
            public void handle(IDefaultRequestContext context) {
                context.response().sendPlainText("Hello World!");
            }
        });

        guice.getInstance(IServer.class).start();
    }
}

Explanation :

  • 9-14 : We can now clearly see that the save(...) method in fact takes a route handler as a parameter. In this simple example, the route handler is declared inline using the IDefaultHandler interface. We'll learn more about route handlers in the next example.

{#========================================== Hello World (more scalable version) ==========================================#}

2. A more scalable version

We're not big fans of those very short "Hello world!" examples because, in real life, you usually don't develop such basic applications. You want an architecture that is flexible and scalable! So, let's use a more flexible structure now.

In this second example we'll:

  • Make the App class itself being managed by Guice.
  • Use a custom Guice module.
  • Learn how to extend the request context type.

If you look carefully, in the previous example we used a lot of Default components ("IDefaultRouter" for example). Those Default components are useful to have a quick up and running Spincast application. But they hide the fact that they represent objects that are actually parameterized. In Spincast, most of the components related to the routing process are parameterized with the request context type. You can learn more about this topic in the The Request Context section of the documentation, but we'll introduce it briefly here.

A request context is the object Spincast passes to the matching route handlers when a request arrives. This object allows your handlers to access information about the current request (its body, headers, path parameters, etc.) and to build the response that is going to be sent back (its body, headers, http status, etc.). But there are also more generic utility methods on the request context, methods not directly related to the request or the response. Here's a quick example :

{% verbatim %}

public void myHandler(IDefaultRequestContext context) {

    Map<String, Object> params = new HashMap<String, Object>();
    params.put("name", "Stromgol");
    String result = context.templating().evaluate("Hello {{name}}", params);
    
    // Do something with the result...
}
{% endverbatim %}

Explanation :

  • 5 : The templating() "method" is not directly related to the request or the response. It's an add-on that provides utilities to the route handlers.

In our first "Hello World" example, the request context type was IDefaultRequestContext, which is the default. In Spincast, this default type can be extended, you can add extra functionalities to it! Those functionalities can be simple methods or full objects, called add-ons. Most of the time, those add-ons are provided by plugins.

But why would we want to extend the request context?

Imagine a plugin which job is to manage authentification and autorization. Wouldn't it be nice if this plugin could add some extra functionalities to the request context? For example :

router.GET("/").save(new IDefaultHandler() {
    @Override
    public void handle(IDefaultRequestContext context) {

        if(context.auth().isAuthenticated()) {
            String username = context.auth().user().getUsername();
            // ...
        }
    }
});

This is what being able to extend the request context allows.

So in this new and improved "Hello world!" example, we're going to use a custom request context type: IAppRequestContext. We're going to add a simple method to it : translate(String sentence, Locale from, Locale to), which job will be to translate a sentence from one language to another. By adding this new method to our custom request context type, our route handlers will have easy access to it.

Note that, for this second example, we still have to include the org.spincast:spincast-default Maven artifact because, even if we'll use a custom request context type, we'll still be using all the other default components (the default HTTP server, for example).

Let's first have a look at what our new App class will be. We'll create the other required components after.

public class App {

    public static void main(String[] args) {

        Injector guice = Guice.createInjector(new AppModule());

        App app = guice.getInstance(App.class);
        app.start();
    }

    private final IServer server;
    private final IRouter<IAppRequestContext, IDefaultWebsocketContext> router;

    @Inject
    public App(IServer server, IRouter<IAppRequestContext, IDefaultWebsocketContext> router) {
        this.server = server;
        this.router = router;
    }

    public void start() {

        this.router.GET("/").save(new IHandler<IAppRequestContext>() {

            @Override
            public void handle(IAppRequestContext context) {
                String translated = context.translate("Hello World!",
                                                      Locale.ENGLISH,
                                                      Locale.FRENCH);
                context.response().sendPlainText(translated);
            }
        });

        this.server.start();
    }
}

Explanation :

  • 1 : The App class is still the entry point of our application but, in this new example, it is also going to be part of the Guice context!
  • 5 : Instead of using the default SpincastDefaultGuiceModule Guice module provided by Spincast, we use a custom Guice module, AppModule (listed below).
  • 7-8 : When the Guice context is created, we get the App instance and we call its start() method. As you'll see, we'll bind the App class itself in our custom AppModule module, so Guice manages it!
  • 14-18 : Since the App instance is managed by Guice, all the required dependencies will be automatically injected in its constructor. Here, we inject the HTTP server (IServer) and a custom and parameterized router: IRouter<IAppRequestContext, IDefaultWebsocketContext>. We'll see that because the router is parameterized with a custom request context type, "IAppRequestContext", our route handler will have access to a new translate(...) method... You can see that the router is also parameterized with a Websocket context type, but that's not important in this example.
  • 20 : The start() method called at line 8, once Guice context is created.
  • 22 : We add a GET route for the "/" index page. Here, we won't use Java 8 lambdas or method handles so you can clearly see the types involved. Since the router, IRouter<IAppRequestContext, IDefaultWebsocketContext>, is parameterized with our custom request context type, the handler type, IHandler<IAppRequestContext>, will also be.
  • 25 : The handle(...) method receives an instance of our custom request context type, "IAppRequestContext".
  • 26-28 : We can now use our new translate(...) method, available directly on the request context object!
  • 33 : We start the server.

Now that we looked at the new App class, let's create our custom IAppRequestContext type in which the new translate(...) method will be defined :

public interface IAppRequestContext extends IRequestContext<IAppRequestContext> {

    public String translate(String sentence, Locale from, Locale to);

    // Other custom methods and/or add-ons...
}

Explanation :

  • 1 : A custom request context interface must extend the IRequestContext base interface and parameterize it using its own type. To learn more about this, check the Extending the default request context type section of the documentation.
  • 3 : We declare our custom translate(...) method.

Then, let's add an implementation for this interface :

public class AppRequestContext extends RequestContextBase<IAppRequestContext>
                               implements IAppRequestContext {

    @AssistedInject
    public AppRequestContext(@Assisted Object exchange,
                             RequestContextBaseDeps<IAppRequestContext> requestContextBaseDeps) {
        super(exchange, requestContextBaseDeps);
    }

    @Override
    public String translate(String sentence, Locale from, Locale to) {

        // Ok, it's more hardcoded than translated for now!
        return "Salut, monde!";
    }
}

Explanation :

  • 1-2 : Our implementation class extends the RequestContextBase class to keep all the functionalities of the default request context, but also implements our custom IAppRequestContext interface, where our new translate(...) method is declared.
  • 4-8 : The constructor is annotated with @AssistedInject because it will be used by a Guice assisted factory. Don't worry too much about this or about the parameters, they are simply required by the parent class's constructor.
  • 10-15 : A dummy implementation of our new method, translate(...).

Finally, we bind everything together by creating a custom Guice module, AppModule. This module is the one used by our App class to create the Guice context.

public class AppModule extends SpincastDefaultGuiceModule {

    @Override
    protected void configure() {
        super.configure();
        bind(App.class).in(Scopes.SINGLETON);
    }

    @Override
    protected Class<? extends IRequestContext<?>> getRequestContextImplementationClass() {
        return AppRequestContext.class;
    }
    
}

Explanation :

  • 1 : Our custom module extends SpincastDefaultGuiceModule since we want to keep the default bindings and only tweak a few things.
  • 6 : In the overidden configure() method, we bind our App class itself! That way, Guice knows about it and is able to inject the dependencies it requires.
  • 9-12 : We override the getRequestContextImplementationClass() method to let Spincast know that it has to use our custom AppRequestContext request context type instead of the default one! Note that Spincast will automatically find the associated IAppRequestContext interface and will use it to parameterize the router and the other routing related components.

And that's it! If you start this application (using the main(...) method of the App class) and point your browser to http://localhost:44419, you'll see "Salut, monde!".

Extending the request context type may seem like a lot of code! But once in place, adding new functionalities is pretty easy. And you'll have a solid and flexible base to build your application.

That said, if it's too much, you can also start using Spincast with the provided default components and only switch to custom ones when you feel ready...

Note that if you use the Quick Start application as a start, the customization of the request context type has already been done for you.

{#========================================== More ==========================================#}

More info

Our second example was a good step forward. But in a real life application, your logic wouldn't all be inside a single App class! You would have controllers, services, respositories, etc.

Make sure you read the Bootstrapping your app section of the documentation for more ideas on how to structure a real life Spincast application.

{% endblock %}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy