Dealing with “java.lang.OutOfMemoryError: PermGen space” error

Recently I ran into this error in my web application:

Recently I ran into this error in my web application:

java.lang.OutOfMemoryError: PermGen space

It's a typical Hibernate/JPA + IceFaces/JSF application running on Tomcat 6 and JDK 1.6. Apparently this can occur after redeploying an application a few times.

What causes it and what can be done to avoid it? How do I fix the problem?

Build a Basic App with Spring Boot and JPA using PostgreSQL

Build a Basic App with Spring Boot and JPA using PostgreSQL

Build a Basic App with Spring Boot and JPA using PostgreSQL. Install PostgreSQL for JPA Persistence. Create a PostgreSQL Database for Your JPA Entities. Build a Spring Boot Resource Server. Add a Domain Class with Spring Data and JPA. Implement a CRUD Repository with Spring Data JPA.

Build a Basic App with Spring Boot and JPA using PostgreSQL. Install PostgreSQL for JPA Persistence. Create a PostgreSQL Database for Your JPA Entities. Build a Spring Boot Resource Server. Add a Domain Class with Spring Data and JPA. Implement a CRUD Repository with Spring Data JPA.

Every non-trivial application needs a way to save and update data: a resource server that is accessible via HTTP. Generally, this data must be secured. Java is a great language with decades of history in professional, enterprise development, and is a great choice for any application’s server stack. Within the Java ecosystem, Spring makes building secure resource servers for your data simple. When coupled with Okta, you get professionally maintained OAuth and JWT technologies easily integrated into Spring Boot using Spring Security.

In this post, you’re going to build a resource server using Spring Boot and Spring Data JPA. On top of that, you’re going to implement a group-based authentication and authorization layer using OAuth 2.0. If this sounds complicated - don’t worry! It’s not.

Before we dig in, let’s cover some background:

A resource server is a programmatic access point for your server’s functions and data (basically the same as an API server and/or possibly REST server).

JPA is the Java Persistence API, a specification for managing relational databases using Java. It describes an abstraction layer between Java classes and a relational database.

Spring Data JPA is a wrapper around JPA providers such as Hibernate. As you’ll see, it makes persisting your Java classes as simple as adding some annotations and creating a simple repository interface. No need to actually write persistence or retrieval methods! Another great benefit is that you can change the underlying database implementation transparently without having to change any code. For example, in this tutorial, you’ll be using Postgres, but later if you decided you’d rather use MySQL, all you’d have to do is change out some dependencies.

Install PostgreSQL for JPA Persistence

You’ll need to have PostgreSQL installed for this tutorial. If you don’t already have it installed, go to their downloads page and install it.

The next thing you’ll need to do is create a Postgres user and database for the project. For this, you can use the Postgres CLI, psql.

You should be able to run the following command: psql -V and get a response like:

psql (PostgreSQL) 11.12


Create a PostgreSQL Database for Your JPA Entities

Before you can use your database, you need to do a few things. You need to:

  1. Create a user for the app
  2. Set a password for that user
  3. Create a database for the app
  4. Grant privileges on the database for the user

This tutorial uses jpatutorial for the username and springbootjpa for the database name. Feel free to change these if you like, but you’ll have to remember to use your custom values throughout the tutorial.

Type psql from the terminal to enter the Postgres shell. Then enter the following command.

Create a user

create user jpatutorial;


The shell should respond with: CREATE ROLE

Don’t forget the semicolon! I would never, ever do this. I am definitely not speaking from experience. But if you don’t type in the semicolon psql doesn’t process the command and you can lose 20-30 minutes in frustrated confusion wondering what is happening until you do enter a semicolon, at which point it tries to process all of your commands.

Give the user a password

alter user jpatutorial with encrypted password '<your really secure password>';


The shell should respond with: ALTER ROLE.

Create the database

create database springbootjpa;


The shell should respond with: CREATE DATABASE.

Grant privileges

grant all privileges on database springbootjpa to jpatutorial;


The shell should respond with GRANT.

Finally, type \q to quit the shell, if you want.

If you want to read more about psql you can take a look at Postgres’ docs.

Build a Spring Boot Resource Server

Clone the starter project from the GitHub repository and check out the start branch:

git clone -b start https://github.com/oktadeveloper/okta-spring-boot-jpa-example.git


The starter project is a clean slate Spring Boot project with just a little bit of Postgres-specific configuration already in place. If you look in the build.gradle file you’ll see a PostgreSQL JPA connector dependency. You’ll also notice the file src/main/resources/hibernate.properties whose sole purpose is to get rid of an annoying warning/error that doesn’t really matter to us. The src/main/resources/application.yml file also has some properties pre-filled for you.

Go ahead and open the application.yml file and fill in the password you created for your database user. You should also update the username, database name, and port (if they are different).

spring:  
  jpa:  
    hibernate:  
      ddl-auto: create  
    database-platform: org.hibernate.dialect.PostgreSQLDialect  
  datasource:  
    url: "jdbc:postgresql://localhost:5432/springbootjpa"  
    username: jpatutorial  
    password: < your password >


The ddl-auto property specifies hibernate’s behavior upon loading. The options are:

  • validate: validates the schema but makes no changes
  • update: updates the schema
  • create: creates the schema, destroying any previous data
  • create-drop: like create, but also drops the schema when the session closes (useful for testing)

You’re using create. Each time the program is run, a new database is created, starting with fresh tables and data.

database-platform is actually unnecessary. Spring Data/Hibernate can autodetect the platform. However, without this property, if you run the app without having started your Postgres server, what you’ll get is a rather unhelpful error about not having added this config property instead of being told to start your server. This happens because Hibernate cannot autodetect the database platform, so complains about that before complaining about there not actually being a running server.

Run the app with ./gradlew bootRun. You should see something like this:

2018-11-21 09:27:50.233  INFO 31888 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Located MBean 'dataSource': registering with JMX server as MBean [com.zaxxer.hikari:name=dataSource,type=HikariDataSource]
2018-11-21 09:27:50.302  INFO 31888 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2018-11-21 09:27:50.308  INFO 31888 --- [           main] c.o.s.SpringBootJpaApplication           : Started SpringBootJpaApplication in 21.361 seconds (JVM running for 21.848)
<=========----> 75% EXECUTING [4m 26s]
> :bootRun


It doesn’t do much just yet, though. There are no domain models, resource repositories, or controller classes.

Add a Domain Class with Spring Data and JPA

A domain or model is the programmatic representation of the data you will be storing. The magic of Spring Data and JPA is that Spring can take a Java class and turn it into a database table for you. It will even autogenerate the necessary load and save methods. The best part is that this is (more or less) database independent.

You’re using PostgreSQL in this tutorial, and you could pretty easily switch it to MySQL if you wanted, just by changing a dependency in the build.gradle file. And, of course, creating a MySQL database and updating the necessary properties in the application.yml file. This is super useful for testing, development, and long-term maintenance.

Keep reading to learn how to develop a simple server to store types of kayaks.

Create a Java file in the com.okta.springbootjpa package called Kayak.java. Your kayak model will have a name, an owner, a value, and a make/model.

package com.okta.springbootjpa;

import lombok.Data;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity // This tells Hibernate to make a table out of this class
@Data // Lombok: adds getters and setters
public class Kayak {

    public Kayak(String name, String owner, Number value, String makeModel) {
        this.name = name;
        this.owner = owner;
        this.value = value;
        this.makeModel = makeModel;
    }

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private Integer id;

    private final String name;

    private String owner;

    private Number value;

    private String makeModel;
}


This project uses Lombok to avoid having to code a bunch of ceremony getters and setters and whatnots. You can check out their docs, or more specifically for the @Data annotation you’re using.

The @Entity annotation is what tells Spring that this class is a model class and should be transformed into a database table.

Most properties can be mapped automatically. The id property, however, is decorated with a couple annotations because we need to tell JPA that this is the ID field and that it should be auto-generated.

Implement a CRUD Repository with Spring Data JPA

With the domain class defined, Spring knows enough to build the database table, but it doesn’t have any controller methods defined. There’s no output or input for the data. Spring makes adding a resource server trivial. In fact, it’s so trivial, you probably won’t believe it.

In the package com.okta.springbootjpa, create an interface called KayakRepository.java.

package com.okta.springbootjpa;

import org.springframework.data.repository.CrudRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;

@RepositoryRestResource
public interface KayakRepository extends CrudRepository<Kayak, Integer> {
}


That’s it!

You can now create, read, update, and delete kayaks from the resource server. In just a sec you’re going to do exactly that, but before you do, make one more change.

Add the following init() method to the SpringBootJpaApplication class:

package com.okta.springbootjpa;

import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

import java.text.NumberFormat;
import java.text.ParseException;
import java.util.stream.Stream;

@SpringBootApplication
public class SpringBootJpaApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootJpaApplication.class, args);
    }

  @Bean
  ApplicationRunner init(KayakRepository repository) {

    String[][] data = {
        {"sea", "Andrew", "300.12", "NDK"},
        {"creek", "Andrew", "100.75", "Piranha"},
        {"loaner", "Andrew", "75", "Necky"}
    };

    return args -> {
      Stream.of(data).forEach(array -> {
        try {
          Kayak kayak = new Kayak(
              array[0],
              array[1],
                  NumberFormat.getInstance().parse(array[2]),
              array[3]
          );
          repository.save(kayak);
        }
        catch (ParseException e) {
          e.printStackTrace();
        }
      });
      repository.findAll().forEach(System.out::println);
    };
  }

}


This method will be run when the application starts. It loads some sample data into the resource server just to give you something to look at in the next section.

Test Your Spring Boot Resource Server

HTTPie is a great command line utility that makes it easy to run requests against the resource server. If you don’t have HTTPie installed, install it using brew install httpie. Or head over to their website and make it happen. Or just follow along.

Make sure your Spring Boot app is running. If it isn’t, start it using ./gradlew bootRun.

Run a GET request against your resource server: http :8080/kayaks, which is shorthand for http GET http://localhost:8080/kayaks.

You’ll see this:

HTTP/1.1 200
Content-Type: application/hal+json;charset=UTF-8
Date: Wed, 21 Nov 2018 20:39:11 GMT
Transfer-Encoding: chunked

{
    "_embedded": {
        "kayaks": [
            {
                "_links": {
                    "kayak": {
                        "href": "http://localhost:8080/kayaks/1"
                    },
                    "self": {
                        "href": "http://localhost:8080/kayaks/1"
                    }
                },
                "makeModel": "NDK",
                "name": "sea",
                "owner": "Andrew",
                "value": 300.12
            },
            {
                "_links": {
                    "kayak": {
                        "href": "http://localhost:8080/kayaks/2"
                    },
                    "self": {
                        "href": "http://localhost:8080/kayaks/2"
                    }
                },
                "makeModel": "Piranha",
                "name": "creek",
                "owner": "Andrew",
                "value": 100.75
            },
            {
                "_links": {
                    "kayak": {
                        "href": "http://localhost:8080/kayaks/3"
                    },
                    "self": {
                        "href": "http://localhost:8080/kayaks/3"
                    }
                },
                "makeModel": "Necky",
                "name": "loaner",
                "owner": "Andrew",
                "value": 75
            }
        ]
    },
    "_links": {
        "profile": {
            "href": "http://localhost:8080/profile/kayaks"
        },
        "self": {
            "href": "http://localhost:8080/kayaks"
        }
    }
}


This output gives you a pretty solid idea of the format of data that the Spring Boot resource returns. You can also add a new kayak using a POST.

Command:

http POST :8080/kayaks name="sea2" owner="Andrew" value="500" makeModel="P&H"


Reply:

HTTP/1.1 201
Content-Type: application/json;charset=UTF-8
Date: Wed, 21 Nov 2018 20:42:14 GMT
Location: http://localhost:8080/kayaks/4
Transfer-Encoding: chunked

{
    "_links": {
        "kayak": {
            "href": "http://localhost:8080/kayaks/4"
        },
        "self": {
            "href": "http://localhost:8080/kayaks/4"
        }
    },
    "makeModel": "P&H",
    "name": "sea2",
    "owner": "Andrew",
    "value": 500
}


If you list the kayaks again (http :8080/kayaks) you’ll see the new kayak among the listed items.

HTTP/1.1 200
Content-Type: application/hal+json;charset=UTF-8
Date: Wed, 21 Nov 2018 20:44:22 GMT
Transfer-Encoding: chunked

{
    "_embedded": {
        "kayaks": [
            ...
            {
                "_links": {
                    "kayak": {
                        "href": "http://localhost:8080/kayaks/4"
                    },
                    "self": {
                        "href": "http://localhost:8080/kayaks/4"
                    }
                },
                "makeModel": "P&H",
                "name": "sea2",
                "owner": "Andrew",
                "value": 500
            }
        ]
    },
    ...
}


You can also delete the kayak. Run this command: http DELETE :8080/kayaks/4 This deletes the kayak with ID = 4, or the kayak we just created. GET the list of kayaks a third time and you’ll see that it’s gone.

With very minimal code, using Spring Boot you can create a fully functioning resource server. This data is being persisted to your Postgres database.

You can verify this by using the Postgres command shell. At the terminal, type psql to enter the shell, then type the following commands.

Connect to the database:

\connect springbootjpa

psql (9.6.2, server 9.6.6)
You are now connected to database "springbootjpa" as user "cantgetnosleep".


Show the table contents:

SELECT * FROM kayak;

 id | make_model |  name  | owner  |                                                                                   value
----+------------+--------+--------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  1 | NDK        | sea    | Andrew | \xaced0005737200106a6176612e6c616e67...8704072c1eb851eb852
  2 | Piranha    | creek  | Andrew | \xaced0005737200106a6176612e6c616e672e...078704059300000000000
  3 | Necky      | loaner | Andrew | \xaced00057372000e6a6176612e6c616e67...7870000000000000004b
  5 | P&H        | sea2   | Andrew | \xaced0005737200116a6176612e6...08b0200007870000001f4
(4 rows)


A couple things to note. First, notice that value is being stored as a binary object because it was defined as a Number type instead of a primitive (double, float, or int). Second, remember that this data is being erased and the entire table is being recreated on every boot of the app because of the ddl-auto: create line in the application.yml file.

Set Up Authentication

Okta is a software-as-service identity, authentication, and authorization provider. While I have definitely worked on projects where outsourcing everything to SaaS providers created more problems than it promised to solve, authentication and authorization is a place where this model makes total sense. Online security is hard. Vulnerabilities are found and servers must be updated quickly. Standards change and code needs modification. All of these changes have the potential to create new vulnerabilities. Letting Okta handle security means that you can worry about the things that make your application unique.

To show you how easy it is to set up, you’re going integrate Okta OAuth and add token-based authentication to the resource server. If you haven’t already, head over to developer.okta.com and sign up for a free account. Once you have an account, open the developer dashboard and create an OpenID Connect (OIDC) application by clicking on the Application top-menu item, and then on the Add Application button.

Select Single-Page App.

The default application settings are great, except that you need to add a Login Redirect URI: a. You’re going to use this in a moment to retrieve a test token.

Also, note your Client ID, as you’ll need that in a moment.

Configure Your Spring Boot Resource Server for Token Authentication

Okta has made adding token authentication to Spring Boot super easy. They have a project called Okta Spring Boot Starter (check out the GitHub project) that simplifies the whole process to a few simple steps.

Add a couple dependencies to your build.gradle file.

compile('org.springframework.security.oauth.boot:spring-security-oauth2-autoconfigure:2.1.0.RELEASE')  
compile('com.okta.spring:okta-spring-boot-starter:0.6.1')


Add the following to the bottom of the build.gradle file (this resolves a logback logging dependency conflict).

configurations.all {  
  exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging'  
  exclude group: 'org.springframework.boot', module: 'logback-classic'  
}


Next, you need to add some configuration to your application.yml file, replacing {yourClientId} with the Client ID from your Okta OIDC application and {yourOktaDomain} with your Okta URL. Something like https://dev-123456.oktapreview.com.

okta:  
  oauth2:  
    issuer: https://{yourOktaDomain}/oauth2/default  
    client-id: {yourClientId}  
    scopes: openid profile email


Finally, you need to add the @EnableResourceServer annotation to your SpringBootVueApplication class.

import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;

@EnableResourceServer  // <- add me
@SpringBootApplication  
public class SpringBootJpaApplication {  

    public static void main(String[] args) {  
        SpringApplication.run(SpringBootJpaApplication.class, args);  
    }
    ...
}


Test the Protected Spring Boot Server

Stop your Spring Boot server and restart it using: ./gradlew bootRun

From the command line, run a simple GET request.

http :8080/kayaks


You’ll get a 401/unauthorized.

HTTP/1.1 401
Cache-Control: no-store
Content-Type: application/json;charset=UTF-8

{
    "error": "unauthorized",
    "error_description": "Full authentication is required to access this resource"
}


Generate an Access Token

To access the server now, you need a valid access token. You can use OpenID Connect Debugger to help you do this. In another window, open oidcdebugger.com.

Authorize URI: https://{yourOktaUrl}/oauth2/default/v1/authorize, with {yourOktaUrl} replaced with your actual Okta preview URL.

Redirect URI: do not change. This is the value you added to your OIDC application above.

Client ID: from the OIDC application you just created.

Scope: openid profile email.

State: any value you want to pass through the OAuth redirect process. I set it to {}.

Nonce: can be left alone. Nonce means “number used once” and is a simple security measure used to prevent the same request being used multiple times.

Response Type: token.

Response mode: form_post.

Click Send Request. If you are not logged into developer.okta.com, then you’ll be required to log in. If you are (as is likely) already logged in, then the token will be generated for your signed-in identity.

Use the Access Token

You use the token by including in an Authorization request header of type Bearer.

Authorization: Bearer eyJraWQiOiJldjFpay1DS3UzYjJXS3QzSVl1MlJZc3VJSzBBYUl3NkU4SDJfNVJr...


To make the request with HTTPie:

http :8080/kayaks 'Authorization: Bearer eyJraWQiOiJldjFpay1DS3UzYjJXS3QzSVl1...'


Add Group-based Authorization

Up until now, the authorization scheme has been pretty binary. Does the request carry a valid token or not. Now you’re going to add Group-based authentication. Note that despite being used interchangeably sometimes on websites of ill repute, roles and groups are not the same thing, and are different ways of implementing authorization.

A role is a collection of collection of permissions that a user can inherit. A group is a collection of users to which a set of standard permissions are assigned. However, in the scope of tokens and how you’re using Spring Security with JPA, the implementation is exactly the same; they’re both passed from the OAuth OIDC application as a string “authority” to Spring, so for the moment they’re essentially interchangeable. The difference would be in what is protected and how they are defined.

To use group-based authorization with Okta, you need to add a “groups” claim to your access token. Create an Admin group (Users > Groups > Add Group) and add your user to it. You can use the account you signed up with, or create a new user (Users > Add Person). Navigate to API > Authorization Servers, click the Authorization Servers tab and edit the default one. Click the Claims tab and Add Claim. Name it “groups”, and include it in the access token. Set the value type to “Groups” and set the filter to be a Regex of .*.

Create a new access token using OIDC Debugger. Take a look at your decoded token by going to jsonwebtoken.io and entering in your generated access token.

The payload will look a bit like this:

{
 "ver": 1,
 "jti": "AT.Hk8lHezJNw4wxey1czypDiNXJUxIlKmdT16MrnLGp9E",
 "iss": "https://dev-533919.oktapreview.com/oauth2/default",
 "aud": "api://default",
 "iat": 1542862245,
 "exp": 1542866683,
 "cid": "0oahpnkb44pcaOIBG0h7",
 "uid": "00ue9mlzk7eW24e8Y0h7",
 "scp": [
  "email",
  "profile",
  "openid"
 ],
 "sub": "[email protected]",
 "groups": [
  "Everyone",
  "Admin"
 ]
}


The groups claim carries the groups to which the user is assigned. The user you’re using to sign into the developer.okta.com website will also be a member of both the “Everyone” group and the “Admin” group.

To get Spring Boot and the resource server to play nicely with group-based authorization, you need to make a few changes to the code.

First, add a new Java class in the com.okta.springbootjpa package called SecurityConfiguration.

package com.okta.springbootjpa;

import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
}


This configuration class is required to enable the @PreAuthorize annotation that you’re going to use to protect the resource server based on group membership.

Next, add the @PreAuthorize annotation to the KayakRepository, like so:

...
import org.springframework.security.access.prepost.PreAuthorize;
...

@RepositoryRestResource  
@PreAuthorize("hasAuthority('Admin')")  
public interface KayakRepository extends CrudRepository<Kayak, Long> {  
}


Finally, in the SpringBootJpaApplication, delete the ApplicationRunner init(KayakRepository repository) method (or just comment out the @Bean annotation). If you skip this step, the build will fail with the following error:

 AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext


The @PreAuthorize annotation actually blocks the init() method from creating the bootstrapped data programmatically because no user is logged in. Thus with the method runs, it throws an error.

Notice that you’re using hasAuthority() in the @PreAuthorize annotation and not hasRole(). The difference is that hasRole() expects groups or roles to be in ALL CAPS and to have a ROLE_ prefix. This can be configured, of course, but hasAuthority() comes without this baggage and simply checks whatever claim you’ve defined as the okta.oauth2.roles-claim in your application.yml.

Test the Admin User in Your Spring Boot App

Restart your Spring Boot app (start with ./gradlew bootRun).

Try an unauthenticated GET request: http :8080/kayaks.

HTTP/1.1 401
Cache-Control: no-store
Content-Type: application/json;charset=UTF-8

{
    "error": "unauthorized",
    "error_description": "Full authentication is required to access this resource"
}


Try it using your token.

Command:

http :8080/kayaks 'Authorization: Bearer eyJraWQiOiJldjFpay1DS3UzYjJXS3QzSVl1MlJZc3VJSzBBYUl3NkU4SDJf...'


Reply:

HTTP/1.1 200
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Content-Type: application/hal+json;charset=UTF-8

{
    "_embedded": {
        "kayaks": []
    },
    "_links": {
        "profile": {
            "href": "http://localhost:8080/profile/kayaks"
        },
        "self": {
            "href": "http://localhost:8080/kayaks"
        }
    }
}


It worked! We don’t have any kayaks because we had to remove the init() method above, so the _embedded.kayaks array is empty.

TIP: going forward, if you don’t want to copy and paste the whole enormous token string, you can store it to a shell variable and reuse it like so:

TOKEN=eyJraWQiOiJldjFpay1DS3UzYjJXS3QzSVl1MlJZc3VJSzBBYUl3NkU4SDJf...
http :8080/kayaks 'Authorization: Bearer $TOKEN'


Create a Non-Admin User

To demonstrate group-based authorization, you need to create a new user on Okta that isn’t an admin. Go to the developer.okta.com dashboard.

From the top-menu, select Users and People.

Click the Add Person button.

Give the user a First Name, Last Name, and Username (which will also be the Primary Email). The values do not matter, and you won’t need to be able to check the email. You simply need to know the email address/username and password so you can log in to Okta in a minute.

Password: change the drop down to Set by admin.

Assign the user a password.

Click Save.

You’ve just created a user that is NOT a member of the Admin group but is a member of the default group Everyone.

Test Group-based Authorization in Your Spring Boot App

Log out of your Okta developer dashboard.

Return to the OIDC Debugger and generate a new token.

This time, log in as your new non-admin user. You’ll be asked to choose a security question, after which you’ll be redirected to the https://oidcdebugger.com/debug page where your token can be copied.

If you like, you can go to jsonwebtoken.io and decode your new token. In the payload, the sub claim will show the email/username of the user, and the groups claim will show only the Everyone group.

{
 ...
 "sub": "[email protected]",
 "groups": [
  "Everyone"
 ]
}


If you use the new token to make a request on the /kayaks endpoint, you’ll get a 403/Access Denied.

http :8080/kayaks 'Authorization: Bearer eyJraWQiOiJldjFpay1DS3UzYjJX...'

HTTP/1.1 403
...

{
    "error": "access_denied",
    "error_description": "Access is denied"
}


To demonstrate the real power of the @PreAuthorize annotation, create a method-level security constraint. Change the KayakRepository class to the following:

@RepositoryRestResource  
public interface KayakRepository extends CrudRepository<Kayak, Long> {  

    @PreAuthorize("hasAuthority('Admin')")  
    <S extends Kayak> S save(S entity);  

}


This restricts only the save() method to members of the Admin group. The rest of the repository will be restricted simply requiring authentication, but no specific group membership.

Restart your Spring Boot server. Run the same request again.

http :8080/kayaks 'Authorization: Bearer eyJraWQiOiJldjFpay1DS3UzYjJX...'

HTTP/1.1 200
...

{
    "_embedded": {
        "kayaks": []
    },
    "_links": {
        "profile": {
            "href": "http://localhost:8080/profile/kayaks"
        },
        "self": {
            "href": "http://localhost:8080/kayaks"
        }
    }
}


The kayaks repository is empty, so _.embedded.kayaks is an empty array.

Try and create a new kayak.

http POST :8080/kayaks name="sea2" owner="Andrew" value="500" makeModel="P&H" "Authorization: Bearer eyJraWQiOiJldjFpay1DS3UzYjJX..."


You’ll get another 403. “Saving” is going to equal an HTML POST here.

However, if you use a token generated from your original, admin account, it’ll work.

NOTE: It’s possible your token will be expired and you’ll have to log out of developer.okta.com again and re-generate the token on the OIDC Debugger.

POST a new kayak with the token generated from your admin account.

This time you’ll get a 201.

HTTP/1.1 201
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Content-Type: application/json;charset=UTF-8
...

{
    "_links": {
        "kayak": {
            "href": "http://localhost:8080/kayaks/1"
        },
        "self": {
            "href": "http://localhost:8080/kayaks/1"
        }
    },
    "makeModel": "P&H",
    "name": "sea2",
    "owner": "Andrew",
    "value": 500
}


Success!

Take a look at Spring Data’s CrudRepository interface to get an idea of the methods that can be overridden and assigned method-level security. The @PreAuthorize annotation can be used with a lot more than just groups, as well. The entire power of Spring’s expression language (SpEL) can be leveraged.

public interface CrudRepository<T, ID> extends Repository<T, ID> {
  <S extends T> S save(S entity);
  <S extends T> Iterable<S> saveAll(Iterable<S> entities);
  Optional<T> findById(ID id);
  boolean existsById(ID id);
  Iterable<T> findAll();
  Iterable<T> findAllById(Iterable<ID> ids);
  long count();
  void deleteById(ID id);
  void delete(T entity);
  void deleteAll(Iterable<? extends T> entities);
  void deleteAll();
}


And that’s it! Pretty cool right? In this tutorial, you set up a PostgreSQL database, created a Spring Boot resource server that used Spring Data and JPA to persist a data model, and then turned this data model into a REST API with shockingly little code. Further, you used Okta to add OIDC authentication and OAuth 2.0 authorization to your server application. And finally, you implemented a simple group-based authorization scheme.

If you’d like to check out this complete project, you can find the repo on GitHub at @oktadeveloper/okta-spring-boot-jpa-example.

Watch out for our next post in this series that will cover using a NoSQL database (MongoDB) with Spring WebFlux.

Learn More

Complete Java Masterclass

Complete Step By Step Java For Testers

Java Web Service Complete Guide - SOAP + REST + Buide App

Selenium WebDriver with Java - Basics to Advanced& Interview

Java Persistence: Hibernate and JPA Fundamentals

Java Swing (GUI) Programming: From Beginner to Expert

Java Basics: Learn to Code the Right Way

The Complete Python & PostgreSQL Developer Course

SQL & Database Design A-Z™: Learn MS SQL Server + PostgreSQL

The Complete SQL Bootcamp

The Complete Oracle SQL Certification Course

SQL for Newbs: Data Analysis for Beginners

Understand to Spring Boot: MySQL and JPA, Hibernate

Understand to Spring Boot: MySQL and JPA, Hibernate

Spring Boot uses Hibernate as the default JPA implementation. The property spring.jpa.hibernate.ddl-auto is used for database initialization. I've used the value “update” for this property.

Before we start, this post is mostly aimed at Java developers, specifically Java developers who use the Spring umbrella of frameworks for development. If you are not a Java developer, you can still read on, chances are you haven't found the love of your life - in terms of languages - yet.

Also, I apologise in advance for the markup mishaps that will pop up here and there in various code sections. You could check out the original blog post here Original Post if you have trouble understanding/copy pasting the code.

First off, what really is Spring?

If you are a java developer, you've probably stumbled upon the term 'Spring' at some point in your journey, and possibly fell face first at either its learning curve, or its complexity. One thing most developers fail to understand - I also struggled with this at first - is that Spring represents a collection of frameworks that are tailored to meet specific development needs. As an example, if you're a java web developer, Spring provides the Web Servlet Framework for web development where Spring MVC (Included in this framework) is built on top of the Servlet API. Therefore, you need not learn all the frameworks that Spring provides, but rather the frameworks that fit your specific use case. Yeah, that's a shortcut, and yes, you're welcome.

If you've never heard about Spring before, Spring is an Inversion of Control and Dependency injection framework. This are fairly big terms but this comprehensive post will help you understand the meanings of these two concepts: IoC and Dependency Injection

Now onto Spring Boot

If you have used Spring MVC before, you've definitely have had to wrestle with Spring MVC's pre-configurations like Setting up the Dispatcher Servlet etc. etc. before you were able to get the framework up and running. This is where Spring Boot comes in. Spring Boot is an auto-configuration tool for setting up your Spring-powered applications. You can now put away those boxing gloves cause you might not need to wrestle with Spring Boot.

To help you understand Spring Boot further, and shine a light on why you should be using it if you already aren't, we'll build a simple Netflix API that allows client devices to register themselves, suggest movies and query movies.

Let us begin

Step 1: Setting up Spring Boot on your application.

Spring offers a project initializer, Spring Initialzr that allows you to select your project specifications and download an already configured Spring Boot project as a zip file or a maven build file. You could skip to step 2 if you have done this.

If you're a more of a hands on type of person who enjoys understanding what's happening under the hood, you can continue with this step.

Folder Structure.

Create a new Java project with you favourite IDE and configure your folder structure to mimic the following design:

└── src
    └── main
        └── controllers
        └── models
        └── repositories
        └── resources
            └── templates
                └── error.html
            └── application.properties
        └── Application.java

contollers - This folder will contain the controllers we define for this project

repositories - This folder will contain the repositories we'll define for our models that will be used to fetch data from the database.

resources - this folder will contain our project resources. The templatesfolder contains our template files that will be rendered by Spring. You can include other folders like static which will be used to server static content like javascript and css files.

Maven dependencies

Spring Boot allows us to include in our pom.xml file all the Spring dependencies that we'll use in our project. Copy paste the following dependencies, together with the Spring Boot Maven Plugin to your pom.xml.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
&lt;groupId&gt;org.springframework&lt;/groupId&gt;
&lt;artifactId&gt;gs-spring-boot&lt;/artifactId&gt;
&lt;version&gt;0.1.0&lt;/version&gt;

&lt;parent&gt;
    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
    &lt;artifactId&gt;spring-boot-starter-parent&lt;/artifactId&gt;
    &lt;version&gt;2.1.6.RELEASE&lt;/version&gt;
&lt;/parent&gt;

&lt;dependencies&gt;
    &lt;!--Spring dependencies--&gt;
&lt;dependency&gt;
    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
    &lt;artifactId&gt;spring-boot-starter-web&lt;/artifactId&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
    &lt;artifactId&gt;spring-boot-starter-thymeleaf&lt;/artifactId&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
    &lt;artifactId&gt;spring-boot-devtools&lt;/artifactId&gt;
    &lt;optional&gt;true&lt;/optional&gt;
&lt;/dependency&gt;

<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
<!--Spring JPA -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
</dependencies>

&lt;properties&gt;
    &lt;java.version&gt;1.8&lt;/java.version&gt;
&lt;/properties&gt;


&lt;build&gt;
    &lt;plugins&gt;
        &lt;plugin&gt;
            &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
            &lt;artifactId&gt;spring-boot-maven-plugin&lt;/artifactId&gt;
        &lt;/plugin&gt;
    &lt;/plugins&gt;
&lt;/build&gt;

</project>

Our dependencies overview:

1. spring-boot-starter-web - When building web applications using java, we often need other external dependencies that we include in our pom.xml like tomcat and Spring MVC. What spring-boot-starter-web does is add all these dependencies through one single dependency.

2. spring-boot-starter-thymeleaf - If you've never used thymeleaf before, thymeleaf is a templating engine for processing and creating HTML, XML, JavaScript, CSS, and text whose template files retain the .html extension and therefore a better alternative to JSPs (Java Server Pages). What this basically means is that you can run thymeleaf template files like normal web pages without a backend server for template processing as in the case of JSPs.

3. spring-boot-devtools - These tools grease your gears of development therefore making the overall development process more bearable. To learn more about what these tools offer, you can check out this link: spring-boot-devtools

4. mysql-connector-java - These is the MySQL JDBC implementation that we'll use to make connections to our MySQL database.

5. spring-boot-starter-data-jpa - Most if not all web applications need some form of persistence, which in java cases, is often JPA (Java Persistence API). If spring-boot-data-jpa is in the classpath, Spring boot will automatically configure our data-source through reading our database configuration from the application.properties file that we will configure next.

Note that we've set our java version to 1.8 since JDK 11 does not offer a lot of things out of the box and therefore you may run into errors like: springboot: org.hibernate.MappingException: Could not get constructor for org.hibernate.persister.entity.SingleTableEntityPersister

Application.properties file

Spring boot automatically reads configuration settings from this file and configures our spring boot environment accordingly. We'll configure our database here and also at the same time disable Spring boot's whitelabel error page which we'll replace with our own custom error page. You can copy paste all this into your own application.properties file if you do not intended to make any changes.

## Spring DATASOURCE (DataSourceAutoConfiguration & DataSourceProperties)
spring.datasource.url = jdbc:mysql://localhost:3306/netflix?useSSL=false
spring.datasource.username = netflix
spring.datasource.password = netflix

Hibernate Properties The SQL dialect makes Hibernate generate better SQL for the chosen database

spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect

Hibernate ddl auto (create, create-drop, validate, update)

spring.jpa.hibernate.ddl-auto = update

#Disabling the whitelabel error page
server.error.whitelabel.enabled=false

In the above application.properties file, We've configured our database, username and password to netflix . You can configure this if you want to. Spring JPA automatically uses the Hibernate implementation of JPA. 

We've set spring.jpa.hibernate.ddl-auto to update which will ensure that any changes we make to our models will be reflected in our Database, which also includes creating a new model. Please note that this option is only suitable for development environments rather than production environments. For more information, you can check this link: Database Initialization.

We've also set server.error.whitelabel.enabled to false to disable Spring boot's whitelabel error pages which we'll replace with our own custom error page.

Configuring our Application.java file

This file will contain the main method which we'll use to ignite our Spring Application with. Copy paste the following to your Application.java file:

package main;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@SpringBootApplication
@EnableJpaRepositories(basePackages="main.repositories")
@EnableTransactionManagement
@EnableJpaAuditing
@EntityScan(basePackages={"main.entities","main.models"})
public class Application {

public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
}

}

@SpringBootApplication is a combination of the following more specific spring annotations -

1. @Configuration : Any class annotated with @Configuration annotation is bootstrapped by Spring and is also considered as a source of other bean definitions.

2. @EnableAutoConfiguration : This annotation tells Spring to automatically configure your application based on the dependencies that you have added in the pom.xml file. For example, If spring-data-jpa or spring-jdbc is in the classpath, then it automatically tries to configure a DataSource by reading the database properties from application.properties file.

3. @ComponentScan : It tells Spring to scan and bootstrap other components defined in the current package (main) and all the sub-packages.

@EnableJpaAuditing is used to support the automatic filling of fields that we'll annotate with @CreatedDate.

@EnableJpaRepositories tells Spring where to find our defined Repositories, since we'll not be using the @Repository annotation.


Step 2. Coding our Controllers.

We'll create only 3 Contollers namely: CustomErrorController that we'll use to format and serve our custom error page, MoviesController that will perform movie related functions and UsersContoller that will perform user related functions.

CustomErrorController

In this controller, we'll register a route error that will be mapped to our renderErrorPage method. Therefore all requests made through the errorroute will be recieved by our method.

Note that here we'll use the @Controller annotation since we'd like to return a view rather than plain text and therefore our method returning a string will return the name of the view. To return plain text rather than views, use the @RestController annotation.

We will also format our error messages to make them more user friendly when we display them on our error page.

We've also implemented the ErrorController interface and overridden the getErrorPath() method which will automatically be invoked when Spring encounters an error.

@Controller
public class CustomErrorController implements ErrorController {

@RequestMapping(value = "error",produces = "application/json;charset=UTF-8")
public String renderErrorPage(HttpServletRequest request, Model model) {
     String errorMsg = "";
    Object status = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
    int httpErrorCode = 404;
    if(status != null){
        httpErrorCode = Integer.valueOf(status.toString());
    }
    switch (httpErrorCode) {
        case 400: {
            errorMsg = "Http Error Code: 400. Bad Request";
            break;
        }
        case 401: {
            errorMsg = "Http Error Code: 401. Unauthorized";
            break;
        }
        case 404: {
            errorMsg = "Http Error Code: 404. Resource not found";
            break;
        }
        case 500: {
            errorMsg = "Http Error Code: 500. Internal Server Error";
            break;
        }
    }
    model.addAttribute("error",errorMsg);
    return "error";
}

@Override
public String getErrorPath() {
    return "/error";
}

}

Movies Controller

As we have stated earlier, this Controller will store functionalities related to our movies.

Since we are creating an api, we'll map api requests to url patterns that start with /api. Therefore, we add a @RequestMapping annotation on top of the class, rather than the method so that every request url we map on our methods will be appended to /api.

@RestController
@RequestMapping(value = "/api",produces = "application/json;charset=UTF-8") //All our api request URLs will start with /api and will return Json
public class MoviesController {

private MoviesRepository moviesRepository;
private CategoriesRepository categoriesRepository;
private UserRepository userRepository;

@Autowired
public MoviesController(MoviesRepository moviesRepository, CategoriesRepository categoriesRepository, UserRepository userRepository){
    this.moviesRepository = moviesRepository;
    this.categoriesRepository = categoriesRepository;
    this.userRepository = userRepository;
}

//Suggest A movie
@GetMapping(value = "/suggestMovie")
public String suggestMovie(@RequestParam(name = "category_id") Long categoryId,@RequestParam(name = "name")String name
,@RequestParam(name = "suggested_by")Long suggestedBy){
    //Movies added through this API route are automatically marked as suggested.
    String movieType = Movies.MovieType.SUGGESTED.getMovieType();
    Movies movies = new Movies();

    //Provided category id should be in our categories table.
    if(categoriesRepository.findById(categoryId).isPresent()){

        if(userRepository.findById(suggestedBy).isPresent()){
            movies.setCategoryId(categoryId);
            movies.setName(name);
            movies.setType(movieType);
            movies.setSuggestedBy(suggestedBy);
            return moviesRepository.save(movies).toString();
        } else {
            return "{'error':'The specified user id does not exist.'}";
        }

    } else {
        return "{'error':'The specified category id does not exist.'}";
    }



}

//delete a suggested movie
@GetMapping(value = "/deleteMovie")
public String deleteMovie(@RequestParam(name = "movie_id") Long movieId,@RequestParam(name = "user_id")Long userId) {
    if(userRepository.findById(userId).isPresent()){
        Optional&lt;Movies&gt; movies = moviesRepository.findById(movieId);
        if(movies.isPresent()){
            List&lt;Movies&gt; movie = moviesRepository.findBySuggestedByEqualsAndIdEquals(userId,movieId);
            if(movie.size()&gt;0){
                moviesRepository.delete(movie.get(0));
                return movie.toString();
            } else {
                return generateErrorResponse("The user specified cannot delete this movie");
            }


        } else {
            return  generateErrorResponse("Specified movie id does not exist");
        }

    } else {
        return generateErrorResponse("Specified user id does not exist");
    }
}

//update a suggested movie. Supports only updating of the movie name or category.
@GetMapping(value = "/updateMovie/{movie_id}")
public String updateMovie(@PathVariable(name = "movie_id") Long movieId,@RequestParam(name = "user_id")Long userId,
                          @RequestParam(name = "movie_name",required = false)String movieName, @RequestParam(name = "movie_category",required = false) Long movieCategory) {
    List&lt;Movies&gt; movie = moviesRepository.findBySuggestedByEqualsAndIdEquals(userId,movieId);
    if(!(movie.size()&gt;0)){
        return generateErrorResponse("The user specified cannot update this movie");
    }

    if(moviesRepository.findById(movieId).isPresent()){
        Movies movies = moviesRepository.findById(movieId).get();
        if(movieName != null &amp;&amp; !movieName.isEmpty()){
            movies.setName(movieName);
        }
        if(movieCategory != null &amp;&amp; categoriesRepository.findById(movieCategory).isPresent()){
            movies.setCategoryId(movieCategory);
        }

        return moviesRepository.save(movies).toString();
    } else {
        return generateErrorResponse("The specified movie id does not exist");
    }
}

//query available movies
@GetMapping(value = "/queryMovies/{categoryId}")
public String queryMovies(@PathVariable Long categoryId,@RequestParam(name = "type") String type){
    JsonObjectBuilder jsonResponse = Json.createObjectBuilder();
    JsonObjectBuilder temp = Json.createObjectBuilder();
    int count = 0;
    for(Movies movie:moviesRepository.findAllByCategoryIdEqualsAndTypeEquals(categoryId,type)){
        temp.add("id",movie.getId());
        temp.add("name",movie.getName());
        temp.add("type",movie.getType());
        temp.add("category_id",movie.getCategoryId());
        temp.add("created_at",movie.getCreatedAt().toString());
        jsonResponse.add(count + "",temp);
        temp = Json.createObjectBuilder();
        count++;
    }

    return jsonResponse.build().toString();
}

private String generateErrorResponse(String message){
    return "{\"error\":\"" + message + "\"";
}

//add categories
@GetMapping(value = "/addCategories")
public String addCategories(@RequestParam(name = "name") String name){
    Categories categories = new Categories();
    categories.setName(name);

    return categoriesRepository.save(categories).toString();
}

}

In this Class, you may have noticed annotations that you might have not seen before. Let's go through them quickly:

1. @Autowired - As the annotation itself suggests, this annotation automatically injects an implementation of the movies, users and categories repository interface which we assign the the field variables we have declared. As we mentioned earlier, you need a repository to be able to access database contents, which explains these three repositories. I'll explain this further when we reach the repositories section.

2. @GetMapping - This annotation is the same as @RequestMapping except that it only maps get requests to the specified url.

3. @RequestParam - This annotation automatically injects the specified query parameter name to this variable.

4. @PathVariable` - This annotation automatically injects the path value - enclosed in curly braces - to this variable.

Users Controller

This controller will contain functionalities related to users. In this case, we'll define only a single method that will be responsible for creating a user.

@RestController

@RequestMapping(value = "/api",produces = "application/json;charset=UTF-8") //All our api request URLs will start with /api and return Json

public class UsersController {

private UserRepository userRepository;

@Autowired
public UsersController(UserRepository userRepository){
this.userRepository = userRepository;
}

@GetMapping(path = "/addUser")
public String addUser(@RequestParam(name = "id")Long id, @RequestParam(name="name") String name) {
Users users = new Users();
users.setId(id);
users.setName(name);

users = userRepository.save(users);
return users.toString();

}
}

Our user IDs in this case will not be auto-generated but instead, we'll provide users with an option to define their own IDs.

Step 3. Defining our Repositories

Repositories will be used by our models to query data from the Database. spring-jpa comes with a JpaRepository interface that defines all CRUD operations that we can perform on an Entity. We'll use the CrudRepositoryimplementation of JpaRespository as it offers many CRUD operations out of the box through methods like findAll(), save() etc. At the same time, CrudRepository automatically generated for us dynamic queries based on method names as we'll see in the following example.

We'll define three repositories for our three entities: CategoriesRepository , MoviesRepository and UsersRepository, which will all be interfaces extending CrudRepository.

CategoriesRepository

public interface CategoriesRepository extends

CrudRepository<Categories,Long> {

}

MoviesRepository

public interface MoviesRepository extends CrudRepository {

List<Movies> findAllByCategoryIdEqualsAndTypeEquals(Long categoryId,String type);

List<Movies> findBySuggestedByEqualsAndIdEquals(Long suggestedBy,Long movieId);

In this repository, notice the abstract methods we have defined. Extending CrudRepository will automatically compel Spring to create an implementation of these methods automatically at run-time just from the definition of the method name. To add Custom methods, we can add them in the following ways:

  1. We can start our query method names with find...Byread...Byquery...Bycount...By, and get...By. Before By we can add expression such as Distinct . After By we need to add property names of our entity.
  2. To get data on the basis of more than one property we can concatenate property names using And and Or while creating method names.
  3. If we want to use completely custom name for our method, we can use @Query annotation to write query.

UsersRepository

@Repository

public interface UserRepository extends CrudRepository {

Final Step: Defining our models.

The models (Entities) that we define will be used to store our table structures as will be defined in the database. We will therefore have three models for our three tables: Categories , Movies and Users.

Categories Model

@Entity

@Table(name = "categories")

public class Categories {

@Id
@GeneratedValue
private Long id;

@NotBlank
private String name;

public Long getId() {
return id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

@Override
public String toString() {
JsonObjectBuilder builder = Json.createObjectBuilder();

//serialize to Json only if the data was persisted.
if(!Objects.isNull(id)){
    builder.add("id",id);
}
if(!Objects.isNull(name)){
    builder.add("name",name);
}

return builder.build().toString();

}

}

`

An entity is a plain old Java object (POJO) class that is mapped to the database and configured for usage through JPA using annotations and/or XML.

Note that we've included a @Table annotation to explicitly define the name of our table. The @Id annotation automatically declares the created field as a primary key for our table in our database. At the same time, the @GeneratedValue annotation will automatically generate a value and store it in the database during saving of a record, pretty much like an auto-increment field. The @NotBlank annotation will automatically validate values that will be inserted into the name variable we defined and ensure that this field is not blank.

We've also defined our own toString method (overriding the superclass's toString method) that will convert our model to a Json string that we return as a response in our controllers.

Movies Model

@Entity

@Table(name = "movies")

@EntityListeners(AuditingEntityListener.class)

@JsonIgnoreProperties(value = {"createdAt"},allowGetters = true)

public class Movies implements Serializable {

@Id
@GeneratedValue
private Long id;

private Long categoryId;

@NotBlank
private String type;

@NotBlank
private String name;

private Long suggestedBy;

@Column(nullable = false, updatable = false)
@Temporal(TemporalType.TIMESTAMP)
@CreatedDate
private Date createdAt; //Stores the date at which a user was created.

@PrePersist
public void prePersist(){
createdAt = new Date();
}

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public Long getCategoryId() {
return categoryId;
}

public void setCategoryId(Long categoryId) {
this.categoryId = categoryId;
}

public String getType() {
return type;
}

public void setType(String type) {
this.type = type;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Date getCreatedAt() {
return createdAt;
}

@Override
public String toString() {
JsonObjectBuilder builder = Json.createObjectBuilder();

//serialize to Json only if the data was persisted.
if(!Objects.isNull(id)){
    builder.add("id",id);
}
if(!Objects.isNull(name)){
    builder.add("name",name);
}

if(!Objects.isNull(categoryId)){
    builder.add("category_id",categoryId);
}

if(!Objects.isNull(createdAt)) {
    builder.add("created_at",createdAt.toString());
}
return builder.build().toString();

}

public Long getSuggestedBy() {
return suggestedBy;
}

public void setSuggestedBy(Long suggestedBy) {
this.suggestedBy = suggestedBy;
}

public enum MovieType{
SUGGESTED("suggested"),ORIGINAL("original");

private String movieType;

 MovieType(String movieType){
    this.movieType = movieType;
}

public String getMovieType() {
    return movieType;
}

}

In this model, note the annotations below:

1. @EntityListeners(AuditingEntityListener.class) - This will attach an entity listener to our model class that will automatically fill the fields we've annotated with @CreatedAt.

2. `@PrePersist - This annotation will ensure that the automatically generated value for the createdAt field is stored in this field whenever we'll need access. For more information on Database Auditing you can check this link: Database Auditing

Users Model

@Entity
@Table(name = "users")
@EntityListeners(AuditingEntityListener.class)
@JsonIgnoreProperties(value = {"createdAt"},
allowGetters = true)
public class Users implements Serializable {
private static final long serialVersionUID = 2L;

@Column(updatable = false)
@Id
private Long id;

@NotBlank(message = "The field 'name' is mandatory.")
private String name;

@Column(nullable = false, updatable = false)
@Temporal(TemporalType.TIMESTAMP)
@CreatedDate
private Date createdAt; //Stores the date at which a user was created.

@PrePersist
public void prePersist(){
    createdAt = new Date();
}

public void setId(long id) {
    this.id = id;
}

public long getId() {
    return id;
}

public void setName(String name) {
    this.name = name;
}

public String getName() {
    return name;
}



@Override
public String toString() {
    JsonObjectBuilder builder = Json.createObjectBuilder();

    //serialize to Json only if the data was persisted.
    if(!Objects.isNull(id)){
        builder.add("id",id);
    }
    if(!Objects.isNull(name)){
        builder.add("name",name);
    }

    if(!Objects.isNull(createdAt)) {
        builder.add("created_at",createdAt.toString());
    }
    return builder.build().toString();
}


public Date getCreatedAt() {
    return createdAt;
}

}

The Custom Error Page Template

In the templates folder we defined, create a html page and name it error.html and copy paste the following code into it:

<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Error</title>
</head>
<body>
    <div>Web Application. Error : th:text="${error}"</div>
</body>
</html>

thymeleaf will automatically parse this html page and render our error message by replacing the th:text attribute.

Finally

Run your Application.java's main method and test out your netflix api on your browser by navigation to localhost:8080/. You should be able to see your json responses on your browser. Alternatively, you can check out my git repository for the source code and a client you can test your code with: github repo

Conclusion

You've successfully made a netflix api using Spring boot, mysql and JPA. Congrats!

Thanks For Visiting, Keep Visiting.

Kotlin Microservices With Micronaut, Spring Cloud, and JPA

Kotlin Microservices With Micronaut, Spring Cloud, and JPA

In this article, we'll learn how to create microservice applications written in Kotlin, using the Micronaut framework, Spring Cloud, and JPA for a little extra help.

In this article, we'll learn how to create microservice applications written in Kotlin, using the Micronaut framework, Spring Cloud, and JPA for a little extra help.

The Micronaut Framework provides support for Kotlin built upon the Kapt compiler plugin. It also implements the most popular cloud-native patterns, like distributed configuration, service discovery, and client-side load balancing. These features allow you to include an application that’s been built on top of Micronaut into an existing microservices-based system. The most popular example of such an approach may be integration with the Spring Cloud ecosystem. If you have already used Spring Cloud, it is very likely you built your microservices-based architecture using the Eureka discovery server and Spring Cloud Config as a configuration server. Beginning with version 1.1, Micronaut supports both these popular tools as part of the Spring Cloud project. That’s good news because, in version 1.0, the only supported distributed solution was Consul, and there was no way to use Eureka discovery together with Consul’s property source (running them together ends with an exception).

In this article, you will learn how to:

  • configure Micronaut Maven support for Kotlin using the Kapt compiler.
  • implement microservices with Micronaut and Kotlin.
  • integrate Micronaut with the Spring Cloud Eureka discovery server.
  • integrate Micronaut with the Spring Cloud Config server.
  • configure JPA/Hibernate support for an application built on top Micronaut.
  • run a single instance of PostgreSQL shared between all sample microservices.

We also have three microservices that communicate with each other. We use Spring Cloud Eureka and Spring Cloud Config for discovery and distributed configuration instead of Consul. Every service has a back-end store — PostgreSQL database. This architecture has been visualized in the following picture.

After that short introduction, we may proceed to the development. Let’s begin from configuring Kotlin support for Micronaut.

1. Kotlin With Micronaut — Configuration

Support for Kotlin with the Kapt compiler plugin is described well on the Micronaut docs site. However, I decided to use Maven instead of Gradle, so our configuration will be slightly different than instructions for Gradle. We configure Kapt inside the Maven plugin for Kotlin, kotlin-maven-plugin. Thanks to that Kapt, we will create Java “stub” classes for each of our Kotlin classes, which can then be processed by Micronaut’s Java annotation processor. The Micronaut annotation processors are declared inside the tag annotationProcessorPaths, in the configuration section. Here’s the full Maven configuration to provide support for Kotlin. Beside the core library micronaut-inject-java, we also use annotations from tracing, OpenAPI, and JPA libraries.

<plugin>
    <groupId>org.jetbrains.kotlin</groupId>
    <artifactId>kotlin-maven-plugin</artifactId>
    <dependencies>
        <dependency>
            <groupId>org.jetbrains.kotlin</groupId>
            <artifactId>kotlin-maven-allopen</artifactId>
            <version>${kotlin.version}</version>
        </dependency>
    </dependencies>
    <configuration>
        <jvmTarget>1.8</jvmTarget>
    </configuration>
    <executions>
        <execution>
            <id>compile</id>
            <phase>compile</phase>
            <goals>
                <goal>compile</goal>
            </goals>
        </execution>
        <execution>
            <id>test-compile</id>
            <phase>test-compile</phase>
            <goals>
                <goal>test-compile</goal>
            </goals>
        </execution>
        <execution>
            <id>kapt</id>
            <goals>
                <goal>kapt</goal>
            </goals>
            <configuration>
                <sourceDirs>
                    <sourceDir>src/main/kotlin</sourceDir>
                </sourceDirs>
                <annotationProcessorPaths>
                    <annotationProcessorPath>
                        <groupId>io.micronaut</groupId>
                        <artifactId>micronaut-inject-java</artifactId>
                        <version>${micronaut.version}</version>
                    </annotationProcessorPath>
                    <annotationProcessorPath>
                        <groupId>io.micronaut.configuration</groupId>
                        <artifactId>micronaut-openapi</artifactId>
                        <version>${micronaut.version}</version>
                    </annotationProcessorPath>
                    <annotationProcessorPath>
                        <groupId>io.micronaut</groupId>
                        <artifactId>micronaut-tracing</artifactId>
                        <version>${micronaut.version}</version>
                    </annotationProcessorPath>
                    <annotationProcessorPath>
                        <groupId>javax.persistence</groupId>
                        <artifactId>javax.persistence-api</artifactId>
                        <version>2.2</version>
                    </annotationProcessorPath>
                    <annotationProcessorPath>
                        <groupId>io.micronaut.configuration</groupId>
                        <artifactId>micronaut-hibernate-jpa</artifactId>
                        <version>1.1.0.RC2</version>
                    </annotationProcessorPath>
                </annotationProcessorPaths>
            </configuration>
        </execution>
    </executions>
</plugin>

We also should not run maven-compiler-plugin during the compilation phase. The Kapt compiler generates Java classes, so we don’t need to run any other compilators during the build.

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <configuration>
        <proc>none</proc>
        <source>1.8</source>
        <target>1.8</target>
    </configuration>
    <executions>
        <execution>
            <id>default-compile</id>
            <phase>none</phase>
        </execution>
        <execution>
            <id>default-testCompile</id>
            <phase>none</phase>
        </execution>
        <execution>
            <id>java-compile</id>
            <phase>compile</phase>
            <goals>
                <goal>compile</goal>
            </goals>
        </execution>
        <execution>
            <id>java-test-compile</id>
            <phase>test-compile</phase>
            <goals>
                <goal>testCompile</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Finally, we will add the Kotlin core library and Jackson module for JSON serialization.

<dependency>
    <groupId>com.fasterxml.jackson.module</groupId>
    <artifactId>jackson-module-kotlin</artifactId>
</dependency>
<dependency>
    <groupId>org.jetbrains.kotlin</groupId>
    <artifactId>kotlin-stdlib-jdk8</artifactId>
    <version>${kotlin.version}</version>
</dependency>

If you are running the application with IntelliJ, you should first enable annotation processing. To do that, go to Build, Execution, Deployment -> Compiler -> Annotation Processors as shown below.

2. Running Postgres

Before proceeding to the development phase, we have to start an instance of the PostgreSQL database. It will be started as a Docker container. For me, PostgreSQL is now available under the address,192.168.99.100:5432, because I’m using Docker Toolbox.

$ docker run -d --name postgres -e POSTGRES_USER=micronaut -e POSTGRES_PASSWORD=123456 -e POSTGRES_DB=micronaut -p 5432:5432 postgres

3. Enabling Hibernate for Micronaut

Hibernate configuration is a little harder for Micronaut than for Spring Boot. We don’t have any projects like Spring Data JPA, where almost everything is auto-configured. Beside specific JDBC drivers for integration with a database, we have to include the following dependencies. We may choose between three available libraries that provide data source implementation: Tomcat, Hikari, or DBCP.

<dependency>
    <groupId>org.postgresql</groupId>
    <artifactId>postgresql</artifactId>
    <version>42.2.5</version>
</dependency>
<dependency>
    <groupId>io.micronaut.configuration</groupId>
    <artifactId>micronaut-jdbc-hikari</artifactId>
</dependency>
<dependency>
    <groupId>io.micronaut.configuration</groupId>
    <artifactId>micronaut-hibernate-jpa</artifactId>
</dependency>
<dependency>
    <groupId>io.micronaut.configuration</groupId>
    <artifactId>micronaut-hibernate-validator</artifactId>
</dependency>

The next step is to provide some configuration settings. All the properties will be stored on the configuration server. We have to set the database connection settings and credentials. The JPA configuration settings are provided under the jpa.* key. We force Hibernate to update the database on application startup and print all the SQL logs (only for tests).

datasources:
  default:
    url: jdbc:postgresql://192.168.99.100:5432/micronaut?ssl=false
    username: micronaut
    password: 123456
    driverClassName: org.postgresql.Driver
jpa:
  default:
    packages-to-scan:
      - 'pl.piomin.services.department.model'
    properties:
      hibernate:
        hbm2ddl:
          auto: update
        show_sql: true

Here’s our sample domain object.

@Entity
data class Department(@Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "department_id_seq") @SequenceGenerator(name = "department_id_seq", sequenceName = "department_id_seq") var id: Long,
                      var organizationId: Long, var name: String) {

    @Transient
    var employees: MutableList<Employee> = mutableListOf()

}

The repository bean needs to inject EntityManager using the @PersistentContext and @CurrentSession annotations. All functions need to be annotated with @Transactional, which does not require the methods to be final (open modifier in Kotlin).

@Singleton
open class DepartmentRepository(@param:CurrentSession @field:PersistenceContext val entityManager: EntityManager) {

    @Transactional
    open fun add(department: Department): Department {
        entityManager.persist(department)
        return department
    }

    @Transactional(readOnly = true)
    open fun findById(id: Long): Department = entityManager.find(Department::class.java, id)

    @Transactional(readOnly = true)
    open fun findAll(): List<Department> = entityManager.createQuery("SELECT d FROM Department d").resultList as List<Department>

    @Transactional(readOnly = true)
    open fun findByOrganization(organizationId: Long) = entityManager.createQuery("SELECT d FROM Department d WHERE d.organizationId = :orgId")
            .setParameter("orgId", organizationId)
            .resultList as List<Department>

}

4. Running Spring Cloud Config Server

Running the Spring Cloud Config server is very simple. I have already described that in some of my previous articles. All those were prepared for Java, while today we start it as Kotlin application. Here’s our main class. It should be annotated with @EnableConfigServer.

@SpringBootApplication
@EnableConfigServer
class ConfigApplication

fun main(args: Array<String>) {
    runApplication<ConfigApplication>(*args)
}

Besides the Kotlin core dependency, we need to include the artifact, spring-cloud-config-server.

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
    <groupId>org.jetbrains.kotlin</groupId>
    <artifactId>kotlin-stdlib-jdk8</artifactId>
    <version>${kotlin.version}</version>
</dependency>

By default, the config server tries to use Git as a properties source backend. We prefer using classpath resources, which is much simpler for our tests. To do that, we have to enable the native profile. We will also set the server port to 8888.

spring:
  application:
    name: config-service
  profiles:
    active: native
server:
  port: 8888

If you place all the configurations under the directory /src/main/resources/config, they will be loaded automatically after the app starts up.

Here’s the configuration file for department-service.

micronaut:
  server:
    port: -1
  router:
    static-resources:
      swagger:
        paths: classpath:META-INF/swagger
        mapping: /swagger/**
datasources:
  default:
    url: jdbc:postgresql://192.168.99.100:5432/micronaut?ssl=false
    username: micronaut
    password: 123456
    driverClassName: org.postgresql.Driver
jpa:
  default:
    packages-to-scan:
      - 'pl.piomin.services.department.model'
    properties:
      hibernate:
        hbm2ddl:
          auto: update
        show_sql: true
endpoints:
  info:
    enabled: true
    sensitive: false
eureka:
  client:
    registration:
      enabled: true
    defaultZone: "localhost:8761"

5. Running a Eureka Server

The Eureka server will also be run as a Spring Boot application written in Kotlin.

@SpringBootApplication
@EnableEurekaServer
class DiscoveryApplication

fun main(args: Array<String>) {
    runApplication<DiscoveryApplication>(*args)
}

We also need to include a single dependency, spring-cloud-starter-netflix-eureka-server, beside kotlin-stdlib-jdk8.

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
    <groupId>org.jetbrains.kotlin</groupId>
    <artifactId>kotlin-stdlib-jdk8</artifactId>
    <version>${kotlin.version}</version>
</dependency>

We run a standalone instance of Eureka on port 8761.

spring:
  application:
    name: discovery-service
server:
  port: 8761
eureka:
  instance:
    hostname: localhost
  client:
    registerWithEureka: false
    fetchRegistry: false
    serviceUrl:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

6. Integrating Micronaut With Spring Cloud

The implementation of a distributed configuration client is automatically included in Micronaut’s core. We only need to include the module for service discovery.

<dependency>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-discovery-client</artifactId>
</dependency>

We don’t have to place anything in the source code. All the features can be enabled via configuration settings. First, we need to enable the config client by setting the micronaut.config-client.enabled property to true. The next step is to enable specific implementations of the config client — in our case, Spring Cloud Config — and then set the target URL.

micronaut:
  application:
    name: department-service
  config-client:
    enabled: true
spring:
  cloud:
    config:
      enabled: true
      uri: http://localhost:8888/

Each application fetches the properties frthe om configuration server. The part of the configuration responsible for enabling discovery based on the Eureka server is visible below.

eureka:
  client:
    registration:
      enabled: true
    defaultZone: "localhost:8761"

7. Running Applications

Kapt needs to be able to compile Kotlin code to Java succesfully. That’s why we place a method inside the class declaration, and annotate it with @JvmStatic. The main class visible below is also annotated with @OpenAPIDefinition in order to generate a Swagger definition for API methods.

@OpenAPIDefinition(
        info = Info(
                title = "Departments Management",
                version = "1.0",
                description = "Department API",
                contact = Contact(url = "https://piotrminkowski.wordpress.com", name = "Piotr Mińkowski", email = "[email protected]")
        )
)
open class DepartmentApplication {

    companion object {
        @JvmStatic
        fun main(args: Array<String>) {
            Micronaut.run(DepartmentApplication::class.java)
        }
    }

}

Here’s the controller class from department-service. It injects a repository bean for database integration and EmployeeClient for HTTP communication with employee-service.

@Controller("/departments")
open class DepartmentController(private val logger: Logger = LoggerFactory.getLogger(DepartmentController::class.java)) {

    @Inject
    lateinit var repository: DepartmentRepository
    @Inject
    lateinit var employeeClient: EmployeeClient

    @Post
    fun add(@Body department: Department): Department {
        logger.info("Department add: {}", department)
        return repository.add(department)
    }

    @Get("/{id}")
    fun findById(id: Long): Department? {
        logger.info("Department find: id={}", id)
        return repository.findById(id)
    }

    @Get
    fun findAll(): List<Department> {
        logger.info("Department find")
        return repository.findAll()
    }

    @Get("/organization/{organizationId}")
    @ContinueSpan
    open fun findByOrganization(@SpanTag("organizationId") organizationId: Long): List<Department> {
        logger.info("Department find: organizationId={}", organizationId)
        return repository.findByOrganization(organizationId)
    }

    @Get("/organization/{organizationId}/with-employees")
    @ContinueSpan
    open fun findByOrganizationWithEmployees(@SpanTag("organizationId") organizationId: Long): List<Department> {
        logger.info("Department find: organizationId={}", organizationId)
        val departments = repository.findByOrganization(organizationId)
        departments.forEach { it.employees = employeeClient.findByDepartment(it.id) }
        return departments
    }

}

It is worth taking a look at the HTTP client implementation. It has been discussed in the details in my last article about Micronaut, A Quick Guide to Microservices with the Micronaut Framework.

@Client(id = "employee-service", path = "/employees")
interface EmployeeClient {

@Get("/department/{departmentId}")
fun findByDepartment(departmentId: Long): MutableList<Employee>

}

You can run all the microservices using IntelliJ. You may also build the whole project with Maven using themvn clean install command, and then run them using the java -jar command. Thanks to maven-shade-plugin, our applications will be generated as uber jars. We then can run them in the following order: config-service, discovery-service, and microservices.

$ java -jar config-service/target/config-service-1.0-SNAPSHOT.jar
$ java -jar discovery-service/target/discovery-service-1.0-SNAPSHOT.jar
$ java -jar employee-service/target/employee-service-1.0-SNAPSHOT.jar
$ java -jar department-service/target/department-service-1.0-SNAPSHOT.jar
$ java -jar organization-service/target/organization-service-1.0-SNAPSHOT.jar

After this, you may take a look at the Eureka dashboard available under the address http://localhost:8761 in order to see a list of the running services. You may also perform some tests by running HTTP API methods.

Summary

The sample application’s source code is available on GitHub in the repository sample-micronaut-microservices in the Kotlin branch. You can refer to that repository for more implementation details that have not been included in this article.