Posted on Leave a comment

Events In EJB Tutorial With Quarkus

Using events to pass messages in EJB can be an useful tool to simplify your business logic. Let’s learn how to fire and listen to events in EJB.

Create and listen to simple events

Let’s imagine that we have just opened a store and when someone visit our shop, we will have some fireworks.

So the logic for the event is: When someone come -> have some firework.

Our first step is to create an object named VisitorComesEvent:

package com.datmt.quarkus.events.events;

public class VisitorComesEvent {

}

It’s a simple object, nothing special.

Now, to fire that event, for example at a rest endpoint, we can do like this:

package com.datmt.quarkus.events;

import com.datmt.quarkus.events.events.VisitorComesEvent;

import javax.enterprise.event.Event;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/store")
public class Store {

    @Inject
    Event<VisitorComesEvent> visitorComesEvent;
    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String hello() {
        visitorComesEvent.fire(new VisitorComesEvent());
        return "Welcome to our shop!";
    }
}

As you can see, when someone visits our shop at /store, the event will be fired.

Now, let’s create a listener to handle the event:

package com.datmt.quarkus.events.events;

import javax.enterprise.event.Observes;

public class VisitorComesObserver {

    public void fireWork(@Observes VisitorComesEvent event) {
        System.out.println("Fireworks!!!!");
    }
}

It’s another simple object. However, the magic here is the annotation @Observer.

When you open the browser and visit /store, the log will says “Fireworks!!!”.

Extending events for specific cases

Now let’s imagine (again) that the store sells toys for kids. When a kid come to the store, in addition to showing the fireworks, we would like to offer our little customers some candies.

Let’s create another event extend the current event called YoungVisitorComes:

package com.datmt.quarkus.events.events;

public class YoungVisitorComesEvent extends VisitorComesEvent{
}

Let’s create another endpoint called /store/young that when someone visits this URL, we will fire YoungVisitorComesEvent instead of VisitorComesEvent:

    @GET
    @Path("/young")
    @Produces(MediaType.TEXT_PLAIN)
    public String hello_youngsters() {
        visitorComesEvent.fire(new YoungVisitorComesEvent());
        return "Welcome to our shop, youngster!";
    }

The VisitorComesObserver now look like this:

package com.datmt.quarkus.events.events;

import javax.enterprise.event.Observes;

public class VisitorComesObserver {

    public void fireWork(@Observes VisitorComesEvent event) {
        System.out.println("Fireworks!!!!");
    }

    public void candies(@Observes YoungVisitorComesEvent event) {
        System.out.println("Here are some candies!");
    }
}

Let’s visit the store as an young customer, we should see Fireworks and also get some candies:

Passing data to the event

Currently, our event objects are quite simple without any properties. Let’s say for example, before showing fireworks and offer candies, we ask the customer’s name so we can print her name on the candies.

Let’s modify YoungVisitorComesEvent like this:

package com.datmt.quarkus.events.events;

public class YoungVisitorComesEvent extends VisitorComesEvent{
    private String name;

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

    public String getName() {
        return name;
    }
}

Now, when firing the event, we pass the customer’s name to the event object:

    @GET
    @Path("/young")
    @Produces(MediaType.TEXT_PLAIN)
    public String hello_youngsters() {
        YoungVisitorComesEvent event = new YoungVisitorComesEvent();
        event.setName("Jane");
        visitorComesEvent.fire(event);
        return "Welcome to our shop, youngster!";
    }

Here, I just hard coded the name of the customer. However, you should pass the name dynamically in real life scenario.

Now, in the event handler, we can get the name of the customer like so:

    public void candies(@Observes YoungVisitorComesEvent event) {
        System.out.println("Here are some candies!" + event.getName());
    }

Visit the URL again, we should get the messages as expected:

Posted on Leave a comment

[Fix] Git Bash Java: invalid target release: 11

So recently I set up a Quarkus project to build some APIs on my new computer, which had JDK 8 installed. When I ran the command:

./mvnw quarkus:dev

I got the following error:

[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  4.848 s
[INFO] Finished at: 2021-05-20T23:28:45+07:00
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project
 quarkus-events: Fatal error compiling: invalid target release: 11 -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoExecutionException

In short, the error complained that in the pom.xml file, I set the target release to version 11:

<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>

However, Maven couldn’t find JDK version 11 on my system. Thus, it produced the error.

Setting environment variable doesn’t work!

I’ve tried changing the SDK to version 11 in Windows’ system environment and also user’s environment variable. However, when checking java version in Git bash, the JDK is still 1.8.

How to fix?

It turned out, the fix is quite simple. All you need to do is to set the environment variable to JDK 11 in ~/.bashrc file!

So, here is the line you need to put in ~/.bashrc:

export JAVA_HOME='/c/Program Files/Java/jdk-11.0.11'

Make sure you replace the path to your own JDK.

Save the file and reload the settings by executing:

source ~/.bashrc

After that, your build should work:

Posted on Leave a comment

Using SecondaryTable For Flexible Database Design (With example)

Let’s consider an example. You have been working on a website selling phones since 2000. In early 2000, there wasn’t any smartphone so you may created an object Phone like this:

@Table(name = "PHONE")
@Entity
@Data
public class Phone {
    @Column(name = "PHONE_ID", nullable = false)
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String model;
    private Float price;
    private Float screenSize;
    private Float weight;
}

I’m using lombok (in case you find @Data strange).

So, back in 2000s, phones were simple like that. However, with the appearance of smartphone. Things get complicated.

To accommodate to the new era, you create a new entity like this:

package com.datmt.jpa2.entity;

import javax.persistence.Entity;

@Entity
public class SmartPhone extends Phone {
    private Integer cpuCoresCount;
    private Boolean hasSDCard;
    private Boolean hasFingerprintUnlock;
    private Float rearCameraResolution;
    private Float frontCameraResolution;
}

All is good.

However, problem arises when you check the database table when inserting a new smartphone.

For example, I inserted two phones, one smart, one isn’t to the table using this code:

 @Override
    public void run(String... args) throws Exception {
        Phone simplePhone = new Phone();
        simplePhone.setModel("NOKIA N8");
        simplePhone.setScreenSize(3.2f);
        simplePhone.setWeight(0.1f);
        simplePhone.setPrice(35.0f);


        SmartPhone smartPhone = new SmartPhone();
        smartPhone.setModel("iPhone 12");
        smartPhone.setPrice(999f);
        smartPhone.setScreenSize(5.7f);
        smartPhone.setWeight(0.5f);
        smartPhone.setCpuCoresCount(8);
        smartPhone.setFrontCameraResolution(20.1f);
        smartPhone.setRearCameraResolution(40.1f);
        smartPhone.setHasSDCard(false);
        smartPhone.setHasFingerprintUnlock(false);


        phoneRepository.save(simplePhone);
        smartPhoneRepository.save(smartPhone);
    }

This is what we have in the database:

many null columns in database

As you can see, a lot of NULL columns.

Who knows what phones will have in the future. Maybe they will have some additional 100 fields. Adding more columns to database isn’t a good strategy.

SecondaryTable to the rescue

There is a very cool feature in JPA that let you put all the extra fields/attributes that are available only in subclasses to a separate table. Thus, you don’t have NULL column and still can represent the relationship between entities.

Let’s modify SmartPhone entity so it’ll use a separate table to store its new attributes.

@Entity
@Data
@SecondaryTable(name = "smart_phone", pkJoinColumns=@PrimaryKeyJoinColumn(name="PHONE_ID"))
public class SmartPhone extends Phone {

    @Column(table = "smart_phone")
    private Integer cpuCoresCount;

    @Column(table = "smart_phone")
    private Boolean hasSDCard;

    @Column(table = "smart_phone")
    private Boolean hasFingerprintUnlock;

    @Column(table = "smart_phone")
    private Float rearCameraResolution;

    @Column(table = "smart_phone")
    private Float frontCameraResolution;
}

Now, if I run the application and check the database, here is the result:

New database reflects secondary table

As you can see, no more null columns!

Let’s get back and discuss the changes.

From the new SmartPhone snippet above, you can see that I’ve added @SecondaryTable annotation specifying some fields of this class will be store on a different table (other than the table of Phone) and the join column is PHONE_ID.

Next, in the fields, if I want to move a particular field to the new table, I simply add a @Column annotation with table point to the new table name.

That’s all it takes to remove the null columns.

I don’t know about you but this make me happy.

Multiple @SecondaryTable

You may wonder if you want to link to another table, would it be @TertiaryTable and so on?

The answer is no. You still use @SecondaryTable to link to as many tables as you want.

So, for example, in the future phones will have brains. Since brains are so divine, we decide to store it to a separate table called brain_phone.

SmartPhone entity now look like this:

@SecondaryTable(name = "smart_phone", pkJoinColumns=@PrimaryKeyJoinColumn(name="PHONE_ID"))
@SecondaryTable(name = "brain_phone", pkJoinColumns=@PrimaryKeyJoinColumn(name="PHONE_ID"))
public class SmartPhone extends Phone {

//...

    @Column(table = "brain_phone")
    private Float brainCount;

}

If we insert a new smartphone with brain count, we’ll get data populated to brain_phone table:

Using more than one secondarytable annotation

You can checkout the code on github to get the full project.