Talk with AWS APIs – AWS SDK for Java

So, you have your application deployed on AWS (maybe is just an EC2 instance or a beanstalk environment) and you need the application to know specific data about the environment it is deployed.

Example given, the app needs to know if it is running on blue or green environment. These two environments are identical and the only difference between them is that blue is running the current application version and green is running the new application version.

In this case you could use AWS APIs to find the CNAME of the application.


For the start we need to add the maven dependencies in pom.xml.

<dependency>
 <groupId>com.amazonaws</groupId>
 <artifactId>aws-java-sdk-elasticbeanstalk</artifactId>
 <version>1.12.415</version>
</dependency>

<dependency>
  <groupId>com.amazonaws</groupId>
  <artifactId>aws-java-sdk-ec2</artifactId>
   <version>1.12.414</version>
</dependency>

Having the AWS libs downloaded, we need to create the AmazonEC2Client to retrieve all info we need form EC2

AmazonEC2Client ec2Client = (AmazonEC2Client) AmazonEC2ClientBuilder.standard()
                .withCredentials(new CustomAWSCredentialsProvider())
                .withRegion(Regions.EU_CENTRAL_1)
                .build();

and an AWSElasticBeanstalkClient

AWSElasticBeanstalkClient beanstalkClient = (AWSElasticBeanstalkClient) AWSElasticBeanstalkClientBuilder.standard()
                .withCredentials(new CustomAWSCredentialsProvider())
                .withRegion(Regions.EU_CENTRAL_1)
                .build();

Get instanceId of EC2 instance the app is running.

final String instanceId = EC2MetadataUtils.getInstanceId();

Given the instanceId we will use the ec2Client to get the tags of ec2 instance with this instanceId.

final DescribeTagsRequest req = new DescribeTagsRequest().withFilters(new Filter("resource-id", Collections.singletonList(instanceId)));

final DescribeTagsResult describeTagsResult = ec2Client.describeTags(req);

final List<TagDescription> tags = describeTagsResult.getTags();

final String envName = tags
     .stream()
     .filter(it -> it.getKey().equalsIgnoreCase("Name"))
     .map(it -> it.getValue())
     .findAny()
     .orElse("");

Now envName has the value of Tag=”Name”. Using the beanstalkClient we will find all environments are running on our AWS account, we will filter them based on envName.

final List<EnvironmentDescription> environments = beanstalkClient.describeEnvironments().getEnvironments();

final String cname = environments.stream()
            .filter(it ->     it.getEnvironmentName().equalsIgnoreCase(envName))
                .map(it -> it.getCNAME())
                .findAny()
                .orElse("");

Great! Having the cname we could know if our app is running on blue env!


You could check the implementation code on

https://github.com/despoina555/CodeExamples/blob/main/src/main/java/aws/AwsClient.java

and the test in

https://github.com/despoina555/CodeExamples/blob/main/src/test/java/org/despina/AwsTest.java

Create an AWS Beanstalk Environment using Terraform

A code example

terraform
/ˈtɛrəfɔːm/
verb: 
terraform something to make a planet more like Earth, so that people can live on it

Oops not this kind of terraform..

If you are a DevOps Engineer or you have widen responsibilities inside your team, probably you have needed to create a server on cloud , or a pipeline or whatever..
And if you use AWS cloud, wow so many configurations and screens etc. mess.

Here comes the Terraform, an infrastructure as code tool that let you define both cloud and on-prem resources in human-readable configuration files that you can version, reuse, and share.

In other words, Terraform is a DevOps tool that you can use to design, create, destroy infrastructure.
Example given you can create applications, environments, networks, servers, pipelines etc.

Terraform provides support for manage resources on Amazon Web Services (AWS), Azure, Google Cloud Platform (GCP), Kubernetes etc.

For more info you can check the official terraform website.


Few months ago, in my team we faced the problem to automate our deployment process and we needed more environments to test the new deliverables with QA team and partners. The environments should be identical and have same build and deployment flow. 
So we decided to use Terraform to create new applications, environments and code pipelines on AWS cloud.

In this post, I will step on the first part of the solution we followed and I will describe the procedure how to build an AWS Beanstalk environment.


Prerequisite:

  • Before we start Download Terraform
  • Assume also that you already have already an AWS account.
    So you already have an:

access_key
secret_key 
region

We need these credentials in order to login on AWS cloud.

Starting:

Let’s see what resources we need to create :
1. An aws elastic beanstalk application 
2. An aws elastic beanstalk environment

May you want to associate an aws key pair . 
A key pair (public key, private key) is used to control login access to EC2 instances. Anyone with the private key, you can SSH into the EC2 machine .

Check here how can create one

We declare all resources inside the resources.tf file and all the variables inside the var.tf file.

So on resources.tf we want to create the elastic beanstalk application and theelastic beanstalk environment that will run a specific application version. Remember more than one environments can belong to one application.

# Create elastic beanstalk application resource 

“aws_elastic_beanstalk_application” “beanstalk-app” { 
   name = var.beanstalk_app_name
}

# Create elastic beanstalk Environment resource 

“aws_elastic_beanstalk_environment” “beanstalk-app-env” {
 name = var.beanstalk_env_name 
 application = var.beanstalk_app_name 
 solution_stack_name = var.solution_stack_name
<... add environment properties... >
}

As you see , we define 
the name of the environment
the application name, 
the solution_stack_name which specifies the platform will be used , in my example “64bit Amazon Linux 2018.03 v3.4.19 running Tomcat 8.5 Java 8”(check the supproted EB platforms here
and of course all the environment properties, as is the aws credentials, the database configuration (here a postgres configuration) etc

The environment properties (in Environment Configuration) can be declared “static”

setting {
  namespace =“aws:elasticbeanstalk:application:environment”
  name = “AWS_ACCESS_KEY_ID”
  value = var.aws_access_key
 }

or “dynamic”

dynamic “setting” {
 for_each = var.settings
  content {
   namespace = setting.value[“namespace”]
   name = setting.value[“name”]
   value = setting.value[“value”]
  }
 }

Go to Action:

Now we have created the resources.tf and var.tf file, we will execute the terraform commands to create the infrastructure.

On terminal, locate to folder that you have created these files.

First of all we need to initialise Terraform

terraform init

Now, terraform should creates an execution plan, detects the changes needed which and lets you preview them . The below command will show the resources that will be created, versioned, or scaled down

terraform plan

If everything looks fine to you, time to apply the changes. Terraform executes the actions scripted in the resources.tf file and creates the infrastructure.

terraform apply

Now assume that you want to destroy all remote objects managed by the configuration file (resources.tf ), execute

terraform destroy

You may want to destroy only some parts of infrastructure, e.g. to destroy the beanstalk environment or the the beanstalk application

terraform destroy -target=aws_elastic_beanstalk_environment.beanstalk-app-env

terraform destroy -target=aws_elastic_beanstalk_application.beanstalk-app

A more complete guide of how you can destroy resources in respect of terraform state you can check how-to-destroy-terraform-resources.

You can check the example source code on GitHub .

Happy “colonize” AWS :))

PostgreSQL : Write a function to iterate over the rows of a table and Update the Data

Assume that you have a table where Subscriptions are stored, example given you have bought a new Spotify subscription, for 3, 6 or 12 months.

In this table , the data that are kept are :

  • the creation date (created_at)
  • last update timestamp (last_updated)
  • start_date of Subscription
  • start_date of Subscription
  • end_date of Subscription
  • duration_in_months (3,6 or 12)
  • customer_id , the customer that bought the subscription

/** Create the table*/
create table subscriptions
(
    id     serial PRIMARY KEY,
    created_at       timestamp,
    last_updated     timestamp,
    start_date       timestamp,
    end_date         timestamp,
    duration_in_months integer,
    customer_id     varchar(255)

);


Let’s feed the table with data

INSERT INTO subscriptions ( created_at, last_updated, start_date, end_date,duration_in_months, customer_id)
VALUES ( NOW(), NOW(), '2022-05-19' ,null,  3, 'customer-1');

INSERT INTO subscriptions ( created_at, last_updated, start_date, end_date, duration_in_months, customer_id)
VALUES ( NOW(), NOW(), '2022-05-19' ,null,  6, 'customer-2');


Problem to Solve:

We want to create a function to iterate over the rows of this table , check the subscription duration and update the end_date column

create or replace function update_dates() returns void
    language plpgsql
AS
$$
DECLARE
    t_curs cursor for
        SELECT *
        FROM subscriptions
        WHERE start_date IS NOT NULL
          AND duration_in_months IS NOT NULL;
    t_row     subscriptions%rowtype;
    _end_date timestamp;

BEGIN

    FOR t_row in t_curs
        LOOP

            RAISE NOTICE 'id: %', t_row.id;
            RAISE NOTICE 'duration_in_months: %', t_row.duration_in_months;
            RAISE NOTICE 'start_date %' , t_row.start_date;

            CASE t_row.duration_in_months
                WHEN 3 THEN _end_date = t_row.start_date + interval '3 months';
                WHEN 6 THEN _end_date = t_row.start_date + interval '3 months';
                WHEN 12 THEN _end_date = t_row.start_date + interval '6 months';
                ELSE _end_date = NULL;
                END CASE;

            RAISE NOTICE '_new_end_date %', _end_date;

            IF _end_date IS NOT NULL THEN
                update subscriptions
                set end_date     = _end_date,
                    last_updated = NOW()
                where current of t_curs;

                RAISE NOTICE 'update subscription with ID  % ', t_row.id;
            END IF;

        END LOOP;
END
$$;

Let’s start explain:

DECLARE
    t_curs cursor for
        SELECT *
        FROM subscriptions
        WHERE start_date IS NOT NULL
          AND duration_in_months IS NOT NULL;
    t_row     subscriptions%rowtype;

Creates a cursor over table subscriptions , for the rows that have start_date and duration_in_months columns not null

 FOR t_row in t_curs
        LOOP
.....
END LOOP;

Creates a loop and iterate over the selected rows of the table

CASE t_row.duration_in_months
                WHEN 3 THEN _end_date = t_row.start_date + interval '3 months';
                WHEN 6 THEN _end_date = t_row.start_date + interval '3 months';
                WHEN 12 THEN _end_date = t_row.start_date + interval '6 months';
                ELSE _end_date = NULL;
                END CASE;

Applies a case selection, based of the value of duration_in_months column for each row, it computes and set value to _end_date variable

 update subscriptions
                set end_date     = _end_date,
                    last_updated = NOW()
                where current of t_curs;

Update the row , with the commuted values

RAISE NOTICE 'update subscription with ID  % ', t_row.id;

RAISE NOTICE is used to print on console

Magic time… execute the function

select update_dates();

The console logs:

your_database_name> select update_dates()
[2022-05-17 23:01:14] [00000] id: 1

[2022-05-17 23:01:14] [00000] duration_in_months: 3
[2022-05-17 23:01:14] [00000] start_date 2022-05-19 00:00:00
[2022-05-17 23:01:14] [00000] _new_end_date 2022-08-19 00:00:00
[2022-05-17 23:01:14] [00000] update subscription with ID  1
[2022-05-17 23:01:14] [00000] id: 2
[2022-05-17 23:01:14] [00000] duration_in_months: 6
[2022-05-17 23:01:14] [00000] start_date 2022-05-19 00:00:00
[2022-05-17 23:01:14] [00000] _new_end_date 2022-08-19 00:00:00
[2022-05-17 23:01:14] [00000] update subscription with ID  2
[2022-05-17 23:01:14] 1 row retrieved starting from 1 in 33 ms (execution: 7 ms, fetching: 26 ms)
ciab.public> select * from subscriptions
[2022-05-17 23:01:21] 2 rows retrieved starting from 1 in 45 ms (execution: 2 ms, fetching: 43 ms)

And then , verify:

select * from subscriptions;

Implement a Redis Cache Service in Java

What is Redis: 
Redis is an open source , in-memory data structure store used as a database, cache, message broker, and streaming engine.
More info in https://redis.io/docs/about/

Jedis is a client library in Java for Redis

Install Redis
Check the instructions in https://redis.io/docs/getting-started/installation/

Java implementation

Add the Maven dependency in pom.xml

<dependency>
 <groupId>redis.clients</groupId>
 <artifactId>jedis</artifactId>
 <version>4.2.2</version>
</dependency>

Create a new class JedisCacheService

private Jedis jedis;
 /**
 * Initialize the cache (default: port 6379 , localhost)
 * if you have started the service on a non-default port or a remote machine,
 * you should configure it by passing the correct values as parameters into the constructor.*/
JedisCacheService(){
 jedis = new Jedis();
}
/** expirationTimeinSeconds = timeout for the specified key. */ void set(String key, String value, int expirationTimeinSeconds) {
 jedis.setex(key, expirationTimeinSeconds, value);
}
String get(String key) {
String data = jedis.get(key);
 if (data != null)
   return data;
return null;
}

Unit Test:

@Test
public void storeDataToRedisCache(){
 int expirationTimeInSeconds = (int) TimeUnit.DAYS.toSeconds(30);
 JedisCacheService jedisCacheService = new JedisCacheService();
 String key = “key-1”;
 String value = “value-1”;
 jedisCacheService.set(key, value,expirationTimeInSeconds);
 String valueInCache = jedisCacheService.get(key);
 Assert.assertEquals(value,valueInCache);
}

Redis CLI

You can verify by checking from command line the data stored in Redis using the redis-cli

redis:6379> keys *
1) “key-1”

redis:6379> mget key-1
1) “value-1”

For more info for redis commands check the documentation https://redis.io/commands/
and for Jedis implemetation the https://javadoc.io/doc/redis.clients/jedis/latest/index.html

Code is also availible in https://github.com/despoina555/CodeExamples/blob/main/src/main/java/org/despina/JedisCacheService.java
and the unit test in
https://github.com/despoina555/CodeExamples/blob/main/src/test/java/org/despina/AppTest.java

Send Multiple Requests Concurrently in Java: Implement CompletableFuture

Asynchronous programming in Action

Modern life is fast.
We eat fast food, demand faster internet, shorter delivery times and instant news. So modern applications need to be fast.
Fast and reliable.

One way to achieve this is by using asynchronous programming.
In Asynchronous programming, main thread continues its execution without to be blocked by separate tasks that run on other threads , when they are completed , main thread is notified about their progress, completion or failure.

Asynchronous programming:
+ improves application performance
+ enhance responsiveness

Introduced in Java 8, CompletableFuture, is a way to perform Asynchronous programming , implements Future interface and provides the ability to complete a future, perform error handling , chain several futures , combine results of multiple futures .

Let’s see an example, nail your keyboards!

Assume that we have a sophisticated application that check the rates between Bitcoin and other currencies and suggest the user which currency to buy, EURO or CHF.

To implement it , I used the investing-cryptocurrency-markets API. 

We will send the 2 requests concurrently and when both responses fetched , we will check which currency have the minimum rate and will return it to the client.

So, we will send 2 CompletableFuture futureBTCtoEURO and futureBTCtoCHF to get the Rates .

final CompletableFuture<Double> futureBTCtoEURO = getRate(BTC,EURO);
final CompletableFuture<Double> futureBTCtoCHF = getRate(BTC, CHF);

getRate(int fromCurrency, int toCurrency) method, will send the request to find the rate to buy fromCurrency, toCurrency and returns a CompletableFuture. In case of exception , return as default 0d.

private CompletableFuture<Double> getRate(int fromCurrency, int toCurrency) {
 return CompletableFuture.supplyAsync(() ->     sendRequest(fromCurrency, toCurrency))
     .exceptionally(exception -> {
      System.err.println(exception);
      return 0d;
 });
}

combinedFuture is created by futureBTCtoEURO and futureBTCtoCHF , waits both features to complete

CompletableFuture<Void> combinedFuture = CompletableFuture.allOf(futureBTCtoEURO, futureBTCtoCHF);

CompletableFuture<Void> combinedFuture = CompletableFuture.allOf(futureBTCtoEURO, futureBTCtoCHF);

combinedFuture.get();

get is a blocking method so waits all futures to be completed

When they are completed, we assign on double basicBTCtoEURO and double basicBTCtoCHF the results of request.
First we validate if the futures are Completed without exception and then we update the value.

double basicBTCtoEURO = 0;
double basicBTCtoCHF = 0;

if (!futureBTCtoEURO.isCompletedExceptionally() && futureBTCtoEURO.isDone()) {
   basicBTCtoEURO = futureBTCtoEURO.get();
 }
 if (!futureBTCtoCHF.isCompletedExceptionally() && futureBTCtoCHF.isDone()) {
   basicBTCtoCHF = futureBTCtoCHF.get();
 }

To sum up:

String getBestRate() {

    int BTC = 189;
    int EURO = 17;
    int CHF = 4;

    double basicBTCtoEURO = 0;
    double basicBTCtoCHF = 0;

    final CompletableFuture<Double> futureBTCtoEURO = getRate(BTC, EURO);
    final CompletableFuture<Double> futureBTCtoCHF = getRate(BTC, CHF);

    // allOf :: waits to complete features futureBTCtoEURO,futureBTCtoCHF
    CompletableFuture<Void> combinedFuture = CompletableFuture.allOf(futureBTCtoEURO, futureBTCtoCHF);
    try {
        combinedFuture.get();//get: blocking method, waits all futures to be completed

        if (!futureBTCtoEURO.isCompletedExceptionally() && futureBTCtoEURO.isDone()) {
            basicBTCtoEURO = futureBTCtoEURO.get();
        }
        if (!futureBTCtoCHF.isCompletedExceptionally() && futureBTCtoCHF.isDone()) {
            basicBTCtoCHF = futureBTCtoCHF.get();
        }

    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (ExecutionException e) {
        e.printStackTrace();
    }
    return findMin(basicBTCtoEURO, basicBTCtoCHF);
}

Let’s run unit test:

@Test
public void sendMultipleRequests(){
    CompletableFutureImpl fut = new CompletableFutureImpl();
    String currency = fut.getBestRate();
    System.out.println( " Buy " + currency);
    Assert.assertNotNull(currency);
 }

Console logs:

Let's get started!
 getRate :fromCurrency  189 to currency 17
sending
 getRate :fromCurrency  189 to currency 4
sending
 getRate :fromCurrency  189 to currency 17 response {"data":[[{"currency_ID":17,"basic":"34910.1","reverse":"0.0000286","digits":1}]]}
 getRate :fromCurrency  189 to currency 4 response {"data":[[{"currency_ID":4,"basic":"36053.4","reverse":"0.0000277","digits":1}]]}
 Buy EURO

Check the code on Github : https://github.com/despoina555/CodeExamples/blob/main/src/main/java/org/despina/CompletableFutureImpl.java

My Git cheat sheet

Git is the most commonly used version control system used among developers. No need to mention its advantages, you can google them!

But, let’s admit it, all developers who love Git, have faced (or still face) difficulties with it and even the most experienced of them keep a cheat sheet to make their life easier.

Excellent practice!

In this post, I would like to share my git-cheat-sheet with the most common commands that I use in my dev-life and I hope to find it useful!

Let’s assume that you have 2 branches : Master and Develop

New Branch Creation

Assume that you have opened the terminal inside a git versioned folder

git fetch --all
git pull

Now the current branch is updated

git checkout -b <Branch>

The new branch is created from the current , this is why we update the current branch

git push origin <Branch>
git checkout -b <branch> <sha>

Creates a new branch from a commit <sha>

git checkout -b newBranch origin/develop

New branch is created from origin/develop

Push branch on Remote Repository (Git Server)

git push origin <Branch>
git push --force

Commit Changes & Push

git add <file_url>
git commit -m “message here”
git push

Delete old branches

git branch -a

Shows all your branches

git remote prune origin

Deletes the remote branches that have been deleted

git branch -D <branch>

Deletes <branch>

Rebase

git fetch --all
git stash
    //stash your local changes (if you have any)git checkout master

git pull
git checkout develop
git rebase master

git push — force 
     //push the rebased changes on origin

git stash pop 
     // pop your local changes back

Reset

git reset --hard HEAD

git fetch origin
git reset --hard origin/develop

git checkout -B develop origin/develop 
    //Resets develop branch to origin/develop , if develop branch was existed on your   local 

Merge Develop Into Master

git fetch --all

git checkout develop
git pull

git checkout master
git pull

git merge — no-ff develop 
     //merges develop into current branch (master) without fast-forward

git push origin

HttpClient with NTLM authentication

Step by step, how to create an HttpClient that supports NTLM authentication in Java

The Problem:

Suppose that we have an instance of Apache HttpClient ( we will use the CloseableHttpClient implementation). We want to perform ΗΤΤP requests to a server that it uses the NTLM authentication security.

Some Theory:

NTLM is a challenge-response authentication protocol which uses three messages to authenticate a client .
Participants:

  • Client
  • Server
  • Domain Controller(DC)

The Flow:

  • The client sends the user name to the server (format DomainName\Username) (Username)
  • The server generates a 16-byte random number, called a Challenge or Nonce, and sends it to the client. (Challenge)
  • The client encrypts this challenge with the hash of the user’s password and returns the result to the server. (Response)
  • The server sends to the DC the Username,Challenge,Response
  • The DC validates if the authentication is successful.

    For more info check the M
    icrosoft Doc

Let’s go to Action!

Maven dependency:

<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.12</version>
</dependency>

The Code:

In order to create the ΝTLM auth provider we need to create:

1. CredentialsProvider

CredentialsProvider credsProvider = new BasicCredentialsProvider();
 credsProvider.setCredentials(AuthScope.ANY, new NTCredentials(“username”, “password”,”workstasion”,”domain name”));

2. AuthSchemeRegistry

Registry<AuthSchemeProvider> authSchemeRegistry = RegistryBuilder.<AuthSchemeProvider>create()
 .register(AuthSchemes.NTLM, new NTLMSchemeFactory())
 .build()

AuthSchemeProvider implementation creates and initializes NTLMScheme instances configured to use the default NTLMEngine implementation. The NTLMEngine can be used to generate Type1 messages and Type3 messages in response to a Type2 challenge

3. Set the TargetPreferredAuthSchemes

RequestConfig config = RequestConfig.custom()
 .setConnectTimeout(connectionTimeout * 1000)
 .setConnectionRequestTimeout(connectionRequestTimeout * 1000)
 .setCookieSpec(CookieSpecs.DEFAULT)
 .setTargetPreferredAuthSchemes(Arrays.asList(AuthSchemes.NTLM,   AuthSchemes.KERBEROS, AuthSchemes.SPNEGO))
 .build();

We give higher priority to NTLM auth schema compare to others

** Notice **
The order is important , also, if you set only setTargetPreferredAuthSchemes(Arrays.asList(AuthSchemes.NTLM)) you will fail to authenticate and will have in logs :
“Authentication scheme Negotiate not supported” . 
This means that the client is only willing to do NTLM while the server is only willing to do Negotiate, thus failing to agree on a common authentication scheme.

4. Build the Client:

CloseableHttpClient httpClient = HttpClientBuilder.create()
 .setDefaultCredentialsProvider(credsProvider)
 .setDefaultAuthSchemeRegistry(authSchemeRegistry)
 .setConnectionManager(new PoolingHttpClientConnectionManager())
 .setDefaultCookieStore(new BasicCookieStore())
 .setDefaultRequestConfig(config)
 .build()

Our client is ready to use!

5. Unit Test

HttpGet httpGet = new HttpGet(“path”);
httpGet.addHeader(“Content-type”, “application/json; charset=utf-8”);
CloseableHttpResponse response = httpClient.execute(httpGet);
Assert.assertEquals(200,response.getStatusLine().getStatusCode());

Check the code in GitHub Repo:
https://github.com/despoina555/CodeExamples
Class
: /src/main/java/org/despina/NtlmAuthImplemetation.java
Unit test: src/test/java/org/despina/AppTest.java

Enums

Sexy, Chic , Powerful

Sexy and Chic because they produce elegant code by constraining the choices of what you can pass.
Powerful, because by limit your choices , actually increase the code effectiveness (it means the code you write today will work if reused 10 years later )

But , first things first…

What is an Enum type? 
They are classes that export one instance for each enumeration constant (think a public static final field ), or consider it as a generalization of singletons ( singleton as an single element enum)

What they Provide?:

  • Compile-time Type Safety:
    Prevent typos and invalid parameters passing in compile-time checking and prevent errors like passing Null values that will fail in Run-time
  • Make testing easier
    If enum is passed as parameter in a method, testing is easier for edge conditions, for example no need to write unit tests for invalid input
  • They are Self-documented
    The set of choices for a parameter is finite and self-describing, so no need to write comments or documentation
  • Can have methods and other fields 
    In Java doc , enum is defined as:

public abstract class Enum<E extends Enum<E>>

extends Object

implements Comparable<E>, Serializable

So you can add methods, fields and implement any behavior you want. **PS** An Enum cannot extend other Enum, all of them extend java.lang.Enum class and Java doesn’t supports multiple inheritance !

When to use them?

  • When you need a set of constants that are known at compile time(e.g transaction statuses, planets , days of week , lipstick color codes )
  • According to Bloch , a single-element enum type is the best way to implement a singleton.
  • Use them to implement the Strategy design pattern
    In this case each enum or many enums can share a behavior.
    Let’s see the below example, the strategy is the ConnectType , enums INSTAGRAM, LINKEDIN, TINDER share the same way to connect, sending a PM, but PHONE and SKYPE follow a different approach.
public enum Networking {

    INSTAGRAM,LINKEDIN, TINDER,
    PHONE(ConnectType.LANDLINE), SKYPE(ConnectType.VoIP);

    private final ConnectType connectType;
    Networking() { this.connectType= ConnectType.TEXT; }
    Networking(ConnectType connectType) {
        this.connectType=connectType;
    }

    void connect(){
        connectType.connect();
    }

    private enum ConnectType {

        TEXT {
            @Override
            void connect() {
                System.out.print("Send a PM ..");
            }
        },

        VoIP {
            @Override
            void connect() {
                System.out.print(" Do a video call! ");
            }
        },

        LANDLINE {
            @Override
            void connect() {
                System.out.print("Classic and Vintage! ");
            }
        };

        abstract void connect();
    }
}