Sunteți pe pagina 1din 13

1.

Introduction
JavaLite is a collection of frameworks for simplifying common tasks that
every developer has to deal with when building applications.
In this tutorial, we’re going to take a look at JavaLite features focused on
building a simple API.

2. Setup
Throughout this tutorial, we’ll create a simple RESTful CRUD application. In
order to do that, we’ll use ActiveWeb and ActiveJDBC – two of the
frameworks that JavaLite integrates with.
So, let’s get started and add the first dependency that we need:
1 <dependency>
2 <groupId>org.javalite</groupId>
3 <artifactId>activeweb</artifactId>
4 <version>1.15</version>
</dependency>
5
ActiveWeb artifact includes ActiveJDBC, so there’s no need to add it
separately. Please note that the latest activeweb version can be found in
Maven Central.
The second dependency we need is a database connector. For this
example, we’re going to use MySQL so we need to add:
1 <dependency>
2 <groupId>mysql</groupId>
3 <artifactId>mysql-connector-java</artifactId>
4 <version>5.1.45</version>
</dependency>
5
Again, latest mysql-connector-java dependency can be found over on Maven
Central.
The last dependency that we have to add is something specific to JavaLite:
1
2 <plugin>
3 <groupId>org.javalite</groupId>
<artifactId>activejdbc-instrumentation</artifactId>
4 <version>1.4.13</version>
5 <executions>
6 <execution>
7 <phase>process-classes</phase>
8 <goals>
<goal>instrument</goal>
9 </goals>
10 </execution>
11 </executions>
12 </plugin>
13
The latest activejdbc-instrumentation plugin can also be found in Maven
Central.
Having all this in place and before starting with entities, tables, and
mappings, we’ll make sure that one of the supported databases is up and
running. As we said before, we’ll use MySQL.
Now we’re ready to start with object-relational mapping.

3. Object-Relational Mapping

3.1. Mapping and Instrumentation


Let’s get started by creating a Product class that will be our main entity:
1 public class Product {}
And, let’s also create the corresponding table for it:
1 CREATE TABLE PRODUCTS (
2 id int(11) DEFAULT NULL auto_increment PRIMARY KEY,
3 name VARCHAR(128)
);
4
Finally, we can modify our Product class to do the mapping:
1 public class Product extends Model {}
We only need to extend org.javalite.activejdbc.Model class. ActiveJDBC
infers DB schema parameters from the database. Thanks to this
capability, there’s no need to add getters and setters or any annotation.
Furthermore, ActiveJDBC automatically recognizes that Product class needs
to be mapped to PRODUCTS table. It makes use of English inflections to
convert singular form of a model to a plural form of a table. And yes, it
works with exceptions as well.
There’s one final thing that we will need to make our mapping work:
instrumentation. Instrumentation is an extra step required by
ActiveJDBC that will allow us to play with our Product class as if it had
getters, setters, and DAO-like methods.
After running instrumentation, we’ll be able to do things like:
1 Product p = new Product();
2 p.set("name","Bread");
3 p.saveIt();

or:
1 List<Product> products = Product.findAll();
This is where activejdbc-instrumentation plugin comes in. As we already
have the dependency in our pom, we should see classes being instrumented
during build:
1 ...
2 [INFO] --- activejdbc-instrumentation:1.4.11:instrument (default) @ javalite ---
3 **************************** START INSTRUMENTATION ****************************
4 Directory: ...\tutorials\java-lite\target\classes
5 Instrumented class: .../tutorials/java-lite/target/classes/app/models/Product.class
**************************** END INSTRUMENTATION ****************************
6 ...
7
Next, we’ll create a simple test to make sure this is working.

3.2. Testing
Finally, to test our mapping, we’ll follow three simple steps: open a
connection to the database, save a new product and retrieve it:
1 @Test
2 public void givenSavedProduct_WhenFindFirst_ThenSavedProductIsReturned() {
3
Base.open(
4 "com.mysql.jdbc.Driver",
5 "jdbc:mysql://localhost/dbname",
6 "user",
7 "password");
8
9 Product toSaveProduct = new Product();
toSaveProduct.set("name", "Bread");
10 toSaveProduct.saveIt();
11
12 Product savedProduct = Product.findFirst("name = ?", "Bread");
13
14 assertEquals(
15 toSaveProduct.get("name"),
16 savedProduct.get("name"));
}
17
18
19
Note that all this (and more) is possible by only having an empty model and
instrumentation.

4. Controllers
Now that our mapping is ready, we can start thinking about our application
and its CRUD methods.
For that, we’re going to make use of controllers which process HTTP
requests.
Let’s create our ProductsController:
1 @RESTful
2 public class ProductsController extends AppController {
3
4 public void index() {
5 // ...
}
6
7 }
8
With this implementation, ActiveWeb will automatically map index() method
to the following URI:
http://<host>:<port>/products
Controllers annotated with @RESTful, provide a fixed set of methods
automatically mapped to different URIs. Let’s see the ones that will be
useful for our CRUD example:

Controller method HTTP method URI


CREATE create() POST http://host:port/products

READ ONE show() GET http://host:port/products/{id}

READ ALL index() GET http://host:port/products

UPDATE update() PUT http://host:port/products/{id}

DELETE destroy() DELETE http://host:port/products/{id}

And if we add this set of methods to our ProductsController:


1
2 @RESTful
3 public class ProductsController extends AppController {
4
5 public void index() {
6 // code to get all products
7 }
8
public void create() {
9 // code to create a new product
10 }
11
12 public void update() {
13 // code to update an existing product
}
14
15 public void show() {
16 // code to find one product
17 }
18
19 public void destroy() {
20 // code to remove an existing product
}
21 }
22
23
Before moving on to our logic implementation, we’ll take a quick look at
few things that we need to configure.

5. Configuration
ActiveWeb is based mostly on conventions, project structure is an example
of that. ActiveWeb projects need to follow a predefined package layout:
1
2 src
|----main
3 |----java.app
4 | |----config
5 | |----controllers
6 | |----models
|----resources
7 |----webapp
8 |----WEB-INF
9 |----views
10
There’s one specific package that we need to take a look at – app.config.
Inside that package we’re going to create three classes:
1 public class DbConfig extends AbstractDBConfig {
2 @Override
3 public void init(AppContext appContext) {
4 this.configFile("/database.properties");
}
5 }
6
This class configures database connections using a properties file in the
project’s root directory containing the required parameters:
1 development.driver=com.mysql.jdbc.Driver
2 development.username=user
3 development.password=password
development.url=jdbc:mysql://localhost/dbname
4
This will create the connection automatically replacing what we did in the
first line of our mapping test.
The second class that we need to include inside app.config package is:
1 public class AppControllerConfig extends AbstractControllerConfig {
2
3 @Override
4 public void init(AppContext appContext) {
5 add(new DBConnectionFilter()).to(ProductsController.class);
}
6
}
7
This code will bind the connection that we just configured to our
controller.
The third class will configure our app’s context:
1 public class AppBootstrap extends Bootstrap {
2 public void init(AppContext context) {}
3 }
After creating the three classes, the last thing regarding configuration
is creating our web.xml file under webapp/WEB-INF directory:
1
2
<?xml version="1.0" encoding="UTF-8"?>
3 <web-app xmlns=...>
4
5 <filter>
6 <filter-name>dispatcher</filter-name>
7 <filter-class>org.javalite.activeweb.RequestDispatcher</filter-class>
<init-param>
8 <param-name>exclusions</param-name>
9 <param-value>css,images,js,ico</param-value>
10 </init-param>
11 <init-param>
12 <param-name>encoding</param-name>
<param-value>UTF-8</param-value>
13 </init-param>
14 </filter>
15
16 <filter-mapping>
17 <filter-name>dispatcher</filter-name>
<url-pattern>/*</url-pattern>
18
</filter-mapping>
19
20 </web-app>
21
22
Now that configuration is done, we can go ahead and add our logic.

6. Implementing CRUD Logic


With the DAO-like capabilities provided by our Product class, it’s super
simple to add basic CRUD functionality:
1 @RESTful
2 public class ProductsController extends AppController {
3
4 private ObjectMapper mapper = new ObjectMapper();
5
public void index() {
6 List<Product> products = Product.findAll();
7 // ...
8 }
9
10 public void create() {
11 Map payload = mapper.readValue(getRequestString(), Map.class);
12 Product p = new Product();
p.fromMap(payload);
13 p.saveIt();
14 // ...
15 }
16
17 public void update() {
18 Map payload = mapper.readValue(getRequestString(), Map.class);
String id = getId();
19 Product p = Product.findById(id);
20 p.fromMap(payload);
21 p.saveIt();
22 // ...
23 }
24
public void show() {
25 String id = getId();
26 Product p = Product.findById(id);
27 // ...
28 }
29
public void destroy() {
30 String id = getId();
31 Product p = Product.findById(id);
32 p.delete();
33 // ...
34 }
}
35
36
37
38
39
40
Easy, right? However, this isn’t returning anything yet. In order to do that,
we have to create some views.

7. Views
ActiveWeb uses FreeMarker as a templating engine, and all its templates
should be located under src/main/webapp/WEB-INF/views.
Inside that directory, we will place our views in a folder
called products (same as our controller). Let’s create our first template
called _product.ftl:
1 {
2 "id" : ${product.id},
"name" : "${product.name}"
3 }
4
It’s pretty clear at this point that this is a JSON response. Of course, this
will only work for one product, so let’s go ahead and create another
template called index.ftl:
1 [<@render partial="product" collection=products/>]
This will basically render a collection named products, with each one
formatted by _product.ftl.
Finally, we need to bind the result from our controller to the
corresponding view:
1
2 @RESTful
3 public class ProductsController extends AppController {
4
5 public void index() {
List<Product> products = Product.findAll();
6 view("products", products);
7 render();
8 }
9
10 public void show() {
String id = getId();
11 Product p = Product.findById(id);
12 view("product", p);
13 render("_product");
14 }
15 }
16
In the first case, we’re assigning products list to our template collection
named also products.
Then, as we’re not specifying any view, index.ftl will be used.
In the second method, we’re assigning product p to element product in the
view and we’re explicitly saying which view to render.
We could also create a view message.ftl:
1 {
2 "message" : "${message}",
3 "code" : ${code}
}
4
And then call it form any of our ProductsController‘s method:
1 view("message", "There was an error.", "code", 200);
2 render("message");

Let’s now see our final ProductsController:


1 @RESTful
2 public class ProductsController extends AppController {
3
private ObjectMapper mapper = new ObjectMapper();
4
5 public void index() {
6 view("products", Product.findAll());
7 render().contentType("application/json");
8 }
9
public void create() {
10 Map payload = mapper.readValue(getRequestString(), Map.class);
11 Product p = new Product();
12 p.fromMap(payload);
13 p.saveIt();
14 view("message", "Successfully saved product id " + p.get("id"), "code", 200);
render("message");
15 }
16
17 public void update() {
18 Map payload = mapper.readValue(getRequestString(), Map.class);
19 String id = getId();
Product p = Product.findById(id);
20 if (p == null) {
21 view("message", "Product id " + id + " not found.", "code", 200);
22 render("message");
23 return;
24 }
p.fromMap(payload);
25 p.saveIt();
26 view("message", "Successfully updated product id " + id, "code", 200);
27 render("message");
28 }
29
public void show() {
30
String id = getId();
31 Product p = Product.findById(id);
32 if (p == null) {
33 view("message", "Product id " + id + " not found.", "code", 200);
34 render("message");
return;
35 }
36 view("product", p);
37 render("_product");
38 }
39
40 public void destroy() {
String id = getId();
41 Product p = Product.findById(id);
42 if (p == null) {
43 view("message", "Product id " + id + " not found.", "code", 200);
44 render("message");
return;
45 }
46 p.delete();
47 view("message", "Successfully deleted product id " + id, "code", 200);
48 render("message");
49 }
50
@Override
51 protected String getContentType() {
52 return "application/json";
53 }
54
55 @Override
56 protected String getLayout() {
return null;
57 }
58 }
59
60
61
62
63
64
65
66
67
68
69
At this point, our application is done and we’re ready to run it.

8. Running the Application


We’ll use Jetty plugin:
1 <plugin>
2 <groupId>org.eclipse.jetty</groupId>
3 <artifactId>jetty-maven-plugin</artifactId>
4 <version>9.4.8.v20171121</version>
</plugin>
5
Find latest jetty-maven-plugin in Maven Central.
And we’re ready, we can run our application:
1 mvn jetty:run
Let’s create a couple of products:
1 $ curl -X POST http://localhost:8080/products
2 -H 'content-type: application/json'
3 -d '{"name":"Water"}'
4 {
5 "message" : "Successfully saved product id 1",
"code" : 200
6 }
7
1 $ curl -X POST http://localhost:8080/products
2 -H 'content-type: application/json'
3 -d '{"name":"Bread"}'
4 {
5 "message" : "Successfully saved product id 2",
"code" : 200
6 }
7
.. read them:
1
2 $ curl -X GET http://localhost:8080/products
[
3 {
4 "id" : 1,
5 "name" : "Water"
6 },
7 {
"id" : 2,
8 "name" : "Bread"
9 }
10 ]
11
.. update one of them:
1 $ curl -X PUT http://localhost:8080/products/1
2 -H 'content-type: application/json'
3 -d '{"name":"Juice"}'
4 {
5 "message" : "Successfully updated product id 1",
"code" : 200
6 }
7
… read the one that we just updated:
1 $ curl -X GET http://localhost:8080/products/1
2 {
3 "id" : 1,
4 "name" : "Juice"
}
5
Finally, we can delete one:
$ curl -X DELETE http://localhost:8080/products/2
1 {
2 "message" : "Successfully deleted product id 2",
3 "code" : 200
}
4
5

9. Conclusion
JavaLite has a lot of tools to help developers get an application up and
running in minutes. However, while basing things on conventions results in
a cleaner and simpler code, it takes a while to understand naming and
location of classes, packages, and files.
This was only an introduction to ActiveWeb and ActiveJDBC, find more
documentation on their website and look for our products application in the
Github project.

S-ar putea să vă placă și