{{announcement.body}}
{{announcement.title}}

What’s New With Jakarta NoSQL? (Part II)

DZone 's Guide to

What’s New With Jakarta NoSQL? (Part II)

The concept of cloud-native and to run an application with this concept using the latest milestone version of Jakarta EE NoSQL.

· Database Zone ·
Free Resource

Moving to Cloud-Native

Cloud computing has brought many methodologies and techniques that have revolutionized both the business and technical worlds. Among the terms that came up were cloud-native. To meet and cover these expectations in the Java universe, Jakarta EE emerged. The purpose of this post is to talk about the concept of cloud-native and to run an application with this concept using the latest milestone version of Jakarta EE NoSQL.

You may also like: From javax.* to jakarta.*: A Simple Proof of Concept

What’s Cloud-Native?

Like any new concept, there are several concepts with the same name; if you read books or articles about cloud-native, you may not find consensus about it. For example:

"Cloud-native is an approach to building and running applications that exploits the advantages of the cloud computing model."
— Pivotal

"Cloud-native is a different way of thinking and reasoning about software systems. It embodies the following concepts: powered by disposable infrastructure, composed of bounded, scales globally, embraces disposable architecture."
— Architecting Cloud Native Applications: Design high-performing and cost-effective applications for the cloud

"In general usage, “cloud-native” is an approach to building and running applications that exploits the advantages of the cloud-computing delivery model. “Cloud-native” is about how applications are created and deployed, not where."
— Infoworld

In a mutual consensus around the definitions from several articles, we can say that cloud-native is a term used to describe container-based environments. So cloud-native isn’t related to specific programming languages or frameworks or even to a cloud provider company, but to containers.

What Are the Cloud-Native Best Practices?

When we start to learn a new concept, we usually run to read about best practices to avoid mistakes and any code smell. With Object-Oriented Programming (OOP), we have the design patterns from the gang of four, in Java, we have Effective Java, and when talking about architecture, we have both Clean Code and Clean Architecture. The question is: what are the best practices for cloud-native?

As far as we know, there aren’t best practices related specifically to cloud-native. Since the cloud is close to Agile methodology, there are several practices we can leverage to have a healthy, pain-free application:

The most well-known practices related to any application that includes cloud computing are inspired by Martin Fowler’s Patterns of Enterprise Application Architecture and Refactoring.

The Twelve-Factor App

  1. Codebase: One codebase tracked in revision control, many deploys.
  2. Dependencies: Explicitly declare and isolate dependencies.
  3. Config: Store config in the environment.
  4. Backing services: Treat backing services as attached resources.
  5. Build, release, run: Strictly separate build and run stages.
  6. Processes: Execute the app as one or more stateless processes.
  7. Port binding: Export services via port binding.
  8. Concurrency: Scale-out via the process model.
  9. Disposability: Maximize robustness with fast startup and graceful shutdown.
  10. Dev/prod parity: Keep development, staging, and production as similar as possible.
  11. Logs: Treat logs as event streams.
  12. Admin processes: Run admin/management tasks as one-off processes.

In summary, there aren’t specific best practices for cloud-native yet, but there are patterns from Agile, microservices, and the twelve-factor app that are useful to follow.

Back to the Code

In the introduction, we explained in detail what cloud-native means, now let’s return to our application and convert it as a cloud-native application. In the first post, we explained the model, the entity, and how Jakarta NoSQL works. So we’ll take it from here and use the easiest way to handle queries with NoSQL and MongoDB with a repository.

Java




x
44


 
1
import jakarta.nosql.mapping.Column;
2
import jakarta.nosql.mapping.Entity;
3
import jakarta.nosql.mapping.Id;
4
 
          
5
import javax.json.bind.annotation.JsonbVisibility;
6
import java.io.Serializable;
7
import java.util.Objects;
8
import java.util.Set;
9
 
          
10
@Entity
11
@JsonbVisibility(FieldPropertyVisibilityStrategy.class)
12
public class Hero implements Serializable {
13
 
          
14
    @Id
15
    private String name;
16
 
          
17
    @Column
18
    private Integer age;
19
 
          
20
    @Column
21
    private Set<String> powers;
22
 
          
23
}
24
 
          
25
 
          
26
import jakarta.nosql.mapping.Page;
27
import jakarta.nosql.mapping.Pagination;
28
import jakarta.nosql.mapping.Repository;
29
 
          
30
import java.util.stream.Stream;
31
 
          
32
public interface HeroRepository extends Repository<Hero, String> {
33
 
          
34
    Stream<Hero> findAll();
35
 
          
36
    Page<Hero> findAll(Pagination pagination);
37
 
          
38
    Stream<Hero> findByPowersIn(String powers);
39
 
          
40
    Stream<Hero> findByAgeGreaterThan(Integer age);
41
 
          
42
    Stream<Hero> findByAgeLessThan(Integer age);
43
}
44
 
          



To make these services available, we’ll create a rest application with JAX-RS as a resource class.

Java




xxxxxxxxxx
1
74


 
1
 
          
2
import javax.enterprise.context.ApplicationScoped;
3
import javax.inject.Inject;
4
import javax.ws.rs.Consumes;
5
import javax.ws.rs.DELETE;
6
import javax.ws.rs.GET;
7
import javax.ws.rs.POST;
8
import javax.ws.rs.PUT;
9
import javax.ws.rs.Path;
10
import javax.ws.rs.PathParam;
11
import javax.ws.rs.Produces;
12
import javax.ws.rs.WebApplicationException;
13
import javax.ws.rs.core.MediaType;
14
import javax.ws.rs.core.Response;
15
import java.util.List;
16
import java.util.function.Supplier;
17
 
          
18
import static java.util.stream.Collectors.toList;
19
 
          
20
@ApplicationScoped
21
@Path("heroes")
22
@Produces(MediaType.APPLICATION_JSON)
23
@Consumes(MediaType.APPLICATION_JSON)
24
public class HeroResource {
25
 
          
26
    private static final Supplier<WebApplicationException> NOT_FOUND =
27
            () -> new WebApplicationException(Response.Status.NOT_FOUND);
28
 
          
29
    @Inject
30
    private HeroRepository repository;
31
 
          
32
    @GET
33
    public List<Hero> findAll() {
34
        return repository.findAll()
35
                .collect(toList());
36
    }
37
 
          
38
    @GET
39
    @Path("/{id}")
40
    public Hero findById(@PathParam("id") String id) {
41
        return repository.findById(id).orElseThrow(NOT_FOUND);
42
    }
43
 
          
44
    @GET
45
    @Path("seniors/{age}")
46
    public List<Hero> findByOlder(@PathParam("age") Integer age) {
47
        return repository.findByAgeGreaterThan(age)
48
                .collect(toList());
49
    }
50
 
          
51
    @GET
52
    @Path("youngs/{age}")
53
    public List<Hero> findByYounger(@PathParam("age") Integer age) {
54
        return repository.findByAgeLessThan(age)
55
                .collect(toList());
56
    }
57
 
          
58
    @POST
59
    public void save(Hero hero) {
60
        repository.save(hero);
61
    }
62
 
          
63
    @PUT
64
    @Path("/{id}")
65
    public void update(@PathParam("id") String id, Hero hero) {
66
        repository.save(hero);
67
    }
68
 
          
69
    @Path("/{id}")
70
    @DELETE
71
    public void delete(@PathParam("id") String name) {
72
        repository.deleteById(name);
73
    }
74
}



The application is ready; the last step we’ll create is the configuration class that allows the connection with MongoDB. This is simple, we’ll use Eclipse MicroProfile Configuration that has tight integration capabilities with Eclipse JNoSQL, the reference implementation of Jakarta NoSQL. The Eclipse MicroProfile Config is a solution to externalize configuration from Java applications and makes the third factor easy to follow.

Java




xxxxxxxxxx
1
27


 
1
import jakarta.nosql.document.DocumentCollectionManager;
2
import org.eclipse.microprofile.config.inject.ConfigProperty;
3
 
          
4
import javax.enterprise.context.ApplicationScoped;
5
import javax.enterprise.inject.Disposes;
6
import javax.enterprise.inject.Produces;
7
import javax.inject.Inject;
8
 
          
9
@ApplicationScoped
10
class DocumentManagerProducer {
11
 
          
12
    @Inject
13
    @ConfigProperty(name = "document")
14
    private DocumentCollectionManager manager;
15
 
          
16
    @Produces
17
    public DocumentCollectionManager getManager() {
18
        return manager;
19
    }
20
 
          
21
    public void destroy(@Disposes DocumentCollectionManager manager) {
22
        manager.close();
23
    }
24
}
25
 
          
26
 
          
27
 
          



The current configuration of an application can be accessed via ConfigProvider#getConfig().

A Config consists of the information collected from the registered org.eclipse.microprofile.config.spi.ConfigSource s. These ConfigSource s get sorted according to their ordinal. That way we can overwrite the configuration with lower importance from outside.

By default there are three ConfigSources:

  • System.getProperties() (ordinal=400).
  • System.getenv() (ordinal=300).
  • all META-INF/microprofile-config.properties files on the ClassPath. (default ordinal=100, separately configurable via a config_ordinal property inside each file).

Therefore, the default values can be specified in the above files packaged with the application and the value can be overwritten later for each deployment. A higher ordinal number takes precedence over a lower number.

That implies we can have the configuration for the local environment as a file, one to test also as a file, and finally, we can override all this information when we move it to the cloud.

Properties files




xxxxxxxxxx
1


 
1
document=document
2
document.database=conferences
3
document.settings.jakarta.nosql.host=localhost:27017
4
document.provider=org.eclipse.jnosql.diana.mongodb.document.MongoDBDocumentConfiguration
5
 
          



We now have a local configuration, so let’s move our application with Jakarta EE based on the cloud-native approach. To make it really easy, we’ll use a Platform-as-a-Service (PaaS) because you can move your application container-based style in cloud through infrastructure as code (IaC).

Infrastructure as code, or programmable infrastructure, means writing code, which can be done using a high-level language or any descriptive language to manage configurations and automate the provisioning of the infrastructure in addition to deployments.

Platform.sh Structure

The Java application is ready to go! The next step is to set the Platform.sh files required to manage and deploy the application. In our first Java post, we took a deep dive into each of these three files in detail:

  • One Router (.platform/routes.yaml). Platform.sh allows you to define the routes.
  • Zero or more service containers (.platform/services.yaml). Platform.sh allows you to completely define and configure the topology and services you want to use on your project.
  • One or more application containers (.platform.app.yaml). You control your application and the way it will be built and deployed on Platform.sh via a single configuration file.

The file that will change on this post is the service file, allowing you to define a database, search engine, cache, and so on. For this project, we’ll set MongoDB instead of MySQL.

YAML




xxxxxxxxxx
1


 
1
mongodb:
2
  type: mongodb:3.6
3
  disk: 1024



To read the environment configuration, Platform.sh supports the configuration-reader that allows easy integration. Platform.sh also supports an array frameworks and languages, including Java. In this post, we’ll override the MongoDB configuration with the Java properties that will add transparency to the application—thanks to the Eclipse MicroProfile Configuration. With these files ready and pushed to the master, Platform.sh will create a set of containers within a cluster.

YAML




xxxxxxxxxx
1
43


 
1
# in the same project.
2
#
3
# See https://docs.platform.sh/user_guide/reference/platform-app-yaml.html
4
 
          
5
# The name of this app. Must be unique within a project.
6
name: app
7
 
          
8
# The runtime the application uses.
9
type: "java:8"
10
 
          
11
disk: 800
12
 
          
13
# The hooks executed at various points in the lifecycle of the application.
14
hooks:
15
  build: |
16
  wget https://github.com/stedolan/jq/releases/download/jq-1.6/jq-linux64
17
  mv jq-linux64 jq
18
  chmod +x jq
19
  mvn -U -DskipTests clean package payara-micro:bundle
20
 
          
21
# The relationships of the application with services or other applications.
22
#
23
# The left-hand side is the name of the relationship as it will be exposed
24
# to the application in the PLATFORM_RELATIONSHIPS variable. The right-hand
25
# side is in the form `<service name>:<endpoint name>`.
26
relationships:
27
  mongodb: 'mongodb:mongodb'
28
 
          
29
# The configuration of app when it is exposed to the web.
30
web:
31
  commands:
32
    start: |
33
      export MONGO_PORT=`echo $PLATFORM_RELATIONSHIPS|base64 -d|json_pp|./jq -r ".mongodb[0].port"`
34
      export MONGO_HOST=`echo $PLATFORM_RELATIONSHIPS|base64 -d|json_pp|./jq -r ".mongodb[0].host"`
35
      export MONGO_ADDRESS="${MONGO_HOST}:${MONGO_PORT}"
36
      export MONGO_PASSWORD=`echo $PLATFORM_RELATIONSHIPS|base64 -d|json_pp|./jq -r ".mongodb[0].password"`
37
      export MONGO_USER=`echo $PLATFORM_RELATIONSHIPS|base64 -d|json_pp|./jq -r ".mongodb[0].username"`
38
      export MONGO_DATABASE=`echo $PLATFORM_RELATIONSHIPS|base64 -d|json_pp|./jq -r ".mongodb[0].path"`
39
      java -jar -Xmx1024m -Ddocument.settings.jakarta.nosql.host=$MONGO_ADDRESS \
40
      -Ddocument.database=$MONGO_DATABASE -Ddocument.settings.jakarta.nosql.user=$MONGO_USER \
41
      -Ddocument.settings.jakarta.nosql.password=$MONGO_PASSWORD \
42
      -Ddocument.settings.mongodb.authentication.source=$MONGO_DATABASE \
43
      target/heroes-microbundle.jar --port $PORT



The application is now ready, so it’s time to move it to the cloud with Platform.sh using the following steps:

  • Create a new free trial account.
  • Sign up with a new username and password, or login using a current GitHub, Bitbucket, or Google account. If you use a third-party login, you’ll be able to set a password for your Platform.sh account later.
  • Select the region of the world where your site should live.
  • Select the blank template.

After this wizard, Platform.sh will provision the whole infrastructure to you, and it will offer your project a remote Git repository. The Platform.sh Git-driven infrastructure means it will automatically manage everything your application needs to push it to the master remote repository. After you set up your SSH keys, you only need to write your code—including a few YAML files that specify your desired infrastructure—then commit it to Git, and push.

Shell




xxxxxxxxxx
1


 
1
git remote add platform <p>
2
git commit -m "Initial project"
3
git push -u platform master
4
 
          



In this post, we talked about the principles and best practices around cloud-native, which is still an area that needs improvement when we’re talking about a new software development technique. Cloud facilitates software development, and we can see an application running quite simply through Platform.sh and integrated with Jakarta EE. All to say, it’s a great time (a happy new year!) to bring your project to a mature cloud PaaS like Platform.s.

Further Reading

Jakarta EE: Generation IV — A New Hope

Jakarta EE: A Clean Slate

Jakarta EE and the Great Naming Debate

Topics:
cloud ,java ,paas ,jakarta ,jakarta nosql ,jnosql ,mongodb ,database ,cloud-native

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}