[Introduction]
Jetty [link] - a lightweight java servlet container. Easily embedded, has a number of artifacts in central maven repository, can be started directly from main method of a Java class.
Jersey [link] - a JAX-RS (Java API for RESTful web-services) implementation. It has neat annotation-based configuration that is somewhat similar to Spring MVC controllers configuration.
Guice [link] - a lightweight dependency-injection framework created by guys from Google. Configuration is done in pure Java without any XML.
Gradle [link] - a modern build automation tool. Can do everything that Maven does and way more. Uses Groovy as a build scripting language (instead of XML, used in maven pom files).
Apart from those big and imposing guys described above, I will use some of not-so-big-and-imposing-but-really-important dudes, but more on that later.
[Code]
I strongly recommend to check any questioned files straight by this link, as I want to keep this tutorial as simple as possible and don't want to quote every single file.
[Tools]
You will need gradle installed ( [link] ).
You can easily follow the steps described further with any IDE of your choice (or even without one), but I recommend using Intellij IDEA (CE will do) or Eclipse with Gradle plug-in to simplify the process.
You will also need Git installed if you wish to clone project sources from Github.
[Build script]
Let's start by creating the standard maven-like directory structure with our java sources situated in ${projectFolder}/src/main/java. Putting java sources in this folder is a convention for gradle projects and you obligated to specify your sources path in your build file if you decide not to follow this convention.
The first file we will create is our gradle build file ${projectFolder}/buid.gradle. The full build gradle can be viewed from GitHub build.gradle. The first lines of the build file are:
apply plugin: 'java' apply plugin: 'application' mainClassName = "graguijer.Main"
Their meaning is simple: we are using java plugin to compile our java sources and application plugin to add a set of specific tasks to our gradle build script specific for standalone java application. We are telling gradle, that our main application class is graguijer.Main, so when we try to invoke "gradle run" from command line, the main method of this class will be executed.
repositories {
mavenCentral()
}
test {
useTestNG()
}
The first closure from the snippet above is instructing gradle to use central maven repo to download dependencies from. The second closure is telling gradle that we will use TestNG for our unit tests instead of JUnit (there will be no unit tests actually, but this part of gradle configuration is provided for reference purposes).
The rest of our build script is a list of dependencies, used in our project. Dependencies are separated into groups using line breaks. The main groups are, respectively:
- language utilities
- logging utilities
- jetty artifacts
- guice artifacts
- jersey artifacts
- freemarker dependency for view processing
- testing artifacts
[Starting jetty web server]
As it was said earlier, you do not need to deploy this application to an external web-server. Gradle downloads jetty artifacts and our Main class runs Jetty server programmatically from its main method:
public static void main(String[] args) throws Exception {
WebAppContext webAppContext = new WebAppContext("web", "/");
webAppContext.setResourceBase("web");
Server server = new Server(8080);
server.setHandler(webAppContext);
server.start();
server.join();
}
Here we create a new Jetty server using "web" folder as our resource root. Then we starting Jetty and joining its thread with our main thread. Simple as that.[Guicy stuff]
Ok, now we have a web application with managed dependencies, which can be easily started. Let's extend our application skeleton by adding dependency injection mechanics to it. All dependency injections will be done by Guice IoC. In order to use Guice in a web application, you need to add the following to your web.xml:
<listener>
<listener-class>graguijer.GuiceServletConfigListener</listener-class>
</listener>
<filter>
<filter-name>guiceFilter</filter-name>
<filter-class>com.google.inject.servlet.GuiceFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>guiceFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Here we declare GuiceServletConfigListener, which is a ServletContexListener. It configures our Injector using ServletModule, so all request that pass though guiceFilter are managed by Guice. Let's take a look inside GuiceServletConfigListener:public class GuiceServletConfigListener extends GuiceServletContextListener {
@Override
protected Injector getInjector() {
return Guice.createInjector(new ServletModule() {
@Override
protected void configureServlets() {
bind(FreemarkerProvider.class);
bind(MainController.class);
serveRegex("/(js|css)/.*")
.with(StaticContentForwardingServlet.class);
serve("/*").with(GuiceContainer.class);
}
});
}
}
We explicitly binding FreemarkerProvider and MainController inside our listener. They are not injected anywhere, but Guice expects them to be bound to process our views and serve our URL. All our static content forwarded to default container servlet using StaticContentForwardingServlet. Also, as you can see, all our non-static content processed by GuiceContainer, which is Jersey container managed by Guice. The cool thing about this container is that uses guice bound beans with @Path annotations to serve URLs specified using Jersey. That's why our MainController must be bound. Let's take a look inside MainController:
@Path("/")
@Singleton
public class MainController {
@GET
@Produces(MediaType.TEXT_HTML)
public Viewable index() {
Map<String, Object> model = Maps.newHashMap();
model.put("string1", "Hello");
model.put("string2", "World");
return new Viewable("index", model);
}
}
As you can see, our Jersey bean serves the root web app path and returns index view with model, containing two strings. Guice expects a ViewProvider to be bound to process views. Luckily, we have FreemarkerProvider (describing its implementation is beyond of the scope of this tutorial - I will leave it to the reader), which will process our ftl templates, namely index.ftl:<html>
<head>
<title>GraGuiJer</title>
<link rel="stylesheet" href="/css/style.css">
</head>
<body>
<div id="helloworld">${string1} ${string2}</div>
</body>
</html>
If you will run this example using main method of the main class and go to http://127.0.0.1:8080/ you will see "Hello World" string in dark red. It means that our web app serves both static (by loading css) and dynamic (by processing our view) content.Hope you will put this information to good use.
Немає коментарів:
Дописати коментар