How to use CompletableFuture in SpringBoot 2

In Spring Boot there is an annotation @Async to assist developers for developing concurrent applications. But using this feature is quite tricky. In this blog we will see how to use this feature along with CompletableFuture. I assumed you know the drill about CompletableFuture, so I won’t repeat the concept here.

First of all you need to annotate your application class with @EnableAsync, this annotation tells the Spring to look for methods that are annotated with @Async and run them in a separate executor.

@SpringBootApplication
@EnableAsync
public class App {
    RestTemplate
    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }
}

If you take a look at Spring Boot example about @Async using CompletableFuture you’ll notice the way they’re using this feature is based on a REST request, in my opinion, I beleive, it’s kinda limited, it doesn’t give you a clue of how to use such feature in other situation. For an instance if you have a long running task what would you do about it?

// Source : https://spring.io/guides/gs/async-method/
package hello;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.concurrent.CompletableFuture;

@Service
public class GitHubLookupService {

    private static final Logger logger = LoggerFactory.getLogger(GitHubLookupService.class);

    private final RestTemplate restTemplate;

    public GitHubLookupService(RestTemplateBuilder restTemplateBuilder) {
        this.restTemplate = restTemplateBuilder.build();
    }

    @Async
    public CompletableFuture<User> findUser(String user) throws InterruptedException {
        logger.info("Looking up " + user);
        String url = String.format("https://api.github.com/users/%s", user);
        User results = restTemplate.getForObject(url, User.class);
        // Artificial delay of 1s for demonstration purposes
        Thread.sleep(1000L);
        return CompletableFuture.completedFuture(results);
    }

}

In FindUser(String user), it uses a synthetic delay in the main thread also the main task of this method is fetching data from github using RestTemplate, this class is a “Synchronous client to perform HTTP requests”. How about using a long running task such as calling a network function, like ping a server from your REST endpoint? In that case you need to tailor the CompletableFuture. You can’t simply call following line and carry on.

return CompletableFuture.completedFuture(results);

How to Use CompletableFuture

For using @Async in your code, your method has to return Future or CompletableFuture for more information you can refer to its document. Take a look at following example :

@Async
    public CompletableFuture<Boolean> isServerAlive(String ip) {
        CompletableFuture<Boolean> future = new CompletableFuture<Boolean>(){
            @Override
            public Boolean get() throws InterruptedException, ExecutionException {
                InetAddress address = null;
                try {
                    address = InetAddress.getByName(ip);
                    return address.isReachable(1000);
                } catch (UnknownHostException e) {
                    e.printStackTrace();
                    return false;
                } catch (IOException e) {
                    e.printStackTrace();
                    return false;
                }
            }
        };
        return future;
}

In this example I override the get() method and return the CompletableFuture without any thread executor, in fact with this method we ask Spring to execute the @Async method in a different thread, but we don’t provide any thread executor, only body of a background-worker will suffice.

download source code from github

P.S : In this example I decided to use a network function inside Spring Boot just for the sake of argument. But it’s better to not to use network functions directly in a REST endpoint. Specially when you expect to get an immediate result out of it. The reason: network functions are blocking which means, if you call this REST endpoint. You’ll have to wait to get the result from the endpoint. It’s highly advised to use other methods such as queue or push method(e.g. websocket) for calling blocking functions.

Installing openLDAP and Making it Replicable

OpenLDAP installation is fairly straight-forward and doesn’t have any caveats, but making it replicable has ambiguity. We will start with installing openLDAP. I will use following configs :

  • ubuntu 16.04 server
  • openLDAP 2.4.x
  • phpLDAPadmin

Installing openLDAP :

First thing first, update your ubuntu box :

sudo apt-get update

Install openLDAP :

sudo apt-get install slapd ldap-utils

During installation process you will prompted to enter administrator password. After installing the ldap server you need to configure it :

sudo dpkg-reconfigure slapd

You will see a basic gui with couple of prompts of how to configuring your openLDAP here is my config:

  • Omit openLDAP Server Configuration : No
  • DNS Domain : your domain in my case, lab.devcrutch.com
  • Organization Name : whatever you fancy, lab
  • Database : MDB (It’s an in-memory database based on BerkeleyDB. In case you were curious)
  • Remove Database when openLDAP is Removed : No
  • Move Old Database : Yes
  • Allow LDAP2 : No

That’s it, if you ever want to check status of your openLDAP :

ldapwhoami -H ldap:// -W -D "cn=admin,dc=lab,dc=devcrutch,dc=com"

This command will prompt you to enter your password and if you enter it correctly you will get following response :

dn:cn=admin,dc=lab,dc=devcrutch,dc=com

You are all set to use openLDAP. Now let’s add an user for replication purposes inside provider (master) node. The replication user only needs to have a password and an OU, run following commands to add repl user with only a password

dn: cn=repl,dc=lab,dc=devcrutch,dc=com
objectClass: simpleSecurityObject
objectClass: organizationalRole
cn: repl
userPassword: {SSHA}Px1UjD+3EMII0g+JZBdZkdO6lhZt4j4k
#password is abc123

Save the above file in an LDIF file and run following command. I will call this file add_repl.ldif

ldapadd -Y EXTERNAL -H ldapi:// -f add_repl.ldif

This user needs to have a privilege to only read couple of items from directory, the most important items to read, is userPassword, cn, uid and shadowLastChange. But before granting such access there is an issue with openLDAP’s configs that shipped by Ubuntu 16.04. It is best to remove those configs using following command :

#Run ldapsearch -Y EXTERNAL -H ldapi:// -b "cn=config"

#Not approapriate configs for making your openLDAP replicable
olcAccess: {0}to attrs=userPassword by self write by anonymous auth by * none
olcAccess: {1}to attrs=shadowLastChange by self write by * read
olcAccess: {2}to * by * read

For deleting them run:

ldapmodify -Y EXTERNAL -H ldapi://

In the prompt write following lines one by one (this way you will delete them step by step for the sake of not getting any error)

dn: olcDatabase={1}mdb,cn=config 
changetype: modify
delete: olcAccess
olcAccess: {0}to attrs=userPassword by self write by anonymous auth by * none
#press enter
dn: olcDatabase={1}mdb,cn=config 
changetype: modify
delete: olcAccess
olcAccess: {0}to attrs=shadowLastChange by self write by * read
#press enter
dn: olcDatabase={1}mdb,cn=config 
changetype: modify
delete: olcAccess
olcAccess: {0}to * by * read
#Press ctrl-d at the end

And add following configs :

#Execute ldapmodify -Y EXTERNAL -H ldapi://
#Then write these configs in it, at end press ctrl-d
dn: olcDatabase={1}mdb,cn=config
changetype: modify
add: olcAccess
olcAccess: {0}to attrs=userPassword,shadowLastChange by self write by anonymous auth by dn="cn=admin,dc=lab,dc=devcrutch,dc=com" write by dn="cn=repl,dc=lab,dc=devcrutch,dc=com" read by * none
#press enter
dn: olcDatabase={1}mdb,cn=config
changetype: modify
add: olcAccess
olcAccess: {1}to dn.base="" by * read
#press enter
dn: olcDatabase={1}mdb,cn=config
changetype: modify
add: olcAccess
olcAccess: {2}to * by self write by dn="cn=admin,dc=lab,dc=devcrutch,dc=com" write by * read

Now your provider is ready. We will go to consumer server. First of all install openLDAP using mentioned configs, it should be the same as the master. At the end add following configs into your consumer’s openLDAP :

#run ldapmodify -Y EXTERNAL -H ldapi://
dn: olcDatabase={1}hdb,cn=config
changetype: modify
add: olcDbIndex
olcDbIndex: entryUUID eq
#press enter
dn: olcDatabase={1}hdb,cn=config
changetype: modify
add: olcDbIndex
olcDbIndex: entryCSN eq
#press enter
dn: olcDatabase={1}hdb,cn=config
changetype: modify
add: olcDbIndex
olcDbIndex: ou eq
#press enter
dn: olcDatabase={1}hdb,cn=config
changetype: modify
add: olcDbIndex
olcDbIndex: uid eq
#press enter
dn: olcDatabase={1}hdb,cn=config
changetype: modify
add: olcDbIndex
olcDbIndex: cn eq
#press enter
dn: olcDatabase={1}hdb,cn=config
changetype: modify
add: olcDbIndex
olcDbIndex: dc eq
#press enter
dn: olcDatabase={1}hdb,cn=config
changetype: modify
add: olcSyncrepl
olcSyncrepl: rid=001 provider="ldap://YOUR_MASTER_IP_ADDRESS:389/" type=refreshAndPersist retry="60 30 300 +" searchbase="dc=lab,dc=devcrutch,dc=com" bindmethod=simple binddn="cn=repl,dc=lab,dc=devcrutch,dc=com" credentials="abc123"

The reason I separate configs is because of stupidity level of ldap tools. If you happen to have one of these configs inside your openLDAP previously, the ldapmodify nags about it and will kick you out without knowing which config is saved and which one isn’t, so the best way for me was saving them sequentially.

Reason for having another user rather the “cn=admin” was because of  security, if you take a closer look at the latter config you will see that you have to add your password as a plain text. So it’s best to not to reveal your admin’s password. The repl user is a readonly user.

At the end you can install phpOpenLDAP in provider and consumer :

sudo apt-get install phpldapadmin

Edit below config :

sudo vim /etc/phpldapadmin/config.php

Change the following :

Find the line contains

$servers->setValue('server','base',array('dc=example,dc=com'));

Change it to

$servers->setValue('server','base',array('dc=lab,dc=devcrutch,dc=com'));

And another line contains

$servers->setValue('login','bind_id','cn=example,dc=com');

Change it to

$servers->setValue('login','bind_id','cn=admin,dc=lab,dc=devcrutch,dc=com');

Note: In this tutorial I’ve tried to create a replication server, replication doesn’t mean you have availability, which means if your master(provider) server is down then your client querys the slave(consumer) server. Replication means consistency not availability. If you happen to want availability you need to config openLDAP in multi-master mode.

How to Configure Iptables in Stateful Mode Properly

Iptables is a software firewall based on Netfilter, in fact it’s a framework for working with Netfilter. Generally firewalls have two modes, stateless and stateful. In this post we will study a brief of how to configure Netfilter in stateful mode.

I’m going to assume your linux box is fresh installation and doesn’t have any rules on it. You can check your iptables rules by typing following command :

sudo iptables -nvL -t filter

Breakdown:

-L : Shows list of rules

-t filter : t stands for table. The table we want to work with is calling  filter, eventhough it’s the default table but I’d rather to mention it

-n : Avoid long reserve DNS and only shows IP addresses

-v : Verbose

Next write following commands :

sudo iptables -A INPUT -p tcp -m state --state RELATED,ESTABLISHED -j ACCEPT
sudo iptables -A INPUT -p tcp -m state --state NEW -j ACCEPT

Rule of Thumb: The order of writing rules matters. You have to take take into account that Netfilter’s rules are checked sequentially and route of a packet is determined on the first match.

The first rule says if the packet is ESTABLISHED or is RELATED to another packet(e.g. ICMP error messages) then it can pass through. If the packet is completely NEW to Netfilter, it skips the first rule and try to match the packet with the second rule. Since ESTABLISHED and RELATED are more frequent, this helps iptables to perform faster by reducing number of rules to check.

Reason: When a client sends a packet to a server, it actually sends a SYN to server. Client’s packet enters into NEW state in Netfilter.

Then server sends a SYN+ACK back to the client, and now it’s client turn to send ACK to the server again. The client is in ESTABLISHED state after sending the ACK.

BreakDown:

-A : Append the rule to following chain, in this case INPUT chain

-p : Protocol (In this case TCP)

-m : Which module we want to use. For making Netfilter stateful we will use state module

–state : Identify the state of packet. This argument comes after the -m state

-j : What action Netfilter has to do with the packet ACCEPT / DROP or REJECT

Note: module state is deprecated and you can use conntack module instead, but according to this post state module is valid yet and no need to be worry about it.

In this post I dived into Netfilter stateful packet filtering and tried to reason why ones need to write rules in such order, of course there are so many stones remained unturned. Hopefully I will write more about Iptables/Netfilter.

Enabling LDAP on Linux and local user’s session will interchange with LDAP’s user intermittently

Let’s say you have a central authentication server in a LDAP and you successfully connect your Linux box as a client to LDAP server. Whenever you are using a particular LDAP user to login to your box, your shell get confused and change the LDAP user with a local user of your box intermittently. What is the problem?

for making it crystal clear lets say your local user ID is : 1000 for knowing the id of user in Ubuntu you can run following command :

id

And this will print out uid, gid and the group id of the user.

Now if you go to your LDAP server and query the same user name  you will notice that his LDAP’s uid is also the same as the local user id 1000.

for solving this problem you either have to change local uid or LDAP uid.

After configuring Ubuntu to use LDAP ssh login become too slow

This is a common symptom of mis behaviour of systemd-logind. I couldn’t still find-out what is really happening underneath of this service but if you experience such issues you can try to restart the service using following command

sudo service systemd-logind restart

and make sure to run this code on the console not just by using ssh.

If anyone knows more about underlying components of systemd-logind please give me some more information about it.

How to install maven on Ubuntu manually with any specific version

For installing Maven on Ubuntu you have two ways  the easy way and the manual method. The easy way is just execute following command :

sudo apt-get install maven

Or if you use Ubuntu 16+ you can use following command too :

sudo apt install maven

Just remember you need to have root privileges.

The manual way

If you want to have the latest version of Maven on your Ubuntu go to the Maven website and download the latest binary package then you can extract it in /opt/  using command :

tar xvf apache-maven-3.5.2-bin.tar.gz -C /opt/

make sure you have set your JAVA_HOME if you don’t know how you can read this post

then open up your /etc/environment add following to your path

PATH=_YOUR_OLD_PATH:/opt/apache-maven-3.5.2/bin/

quit from your editor then type

source /etc/environment

to test if maven is installed type

mvn -v

 

How to install JDK on Ubuntu

Installing Java JDK is easy and straightforward in Windows and OSX but not in Ubuntu. Here is how I am installing JDK on Ubuntu.
Download the latest JDK package from Oracle website. If you don’t know which file to choose you ought to select .tar.gz files whether 32bit or 64bit depends on your requirements.
after download the right package you need to extract it using following command you can extract it :

tar xvf jdk-8u161-linux-x64.tar.gz -C /opt/

Note : In this example I am using JDK version 8 revision 161

The /opt directory is where I chose to extract my JDK.

After extracting my JDK it is time to let Ubuntu know where to look after the Java Development Kit and Java Run Time.

Suppose you want to install your java and javac and javaws executable files in /usr/bin directory :

sudo update-alternatives --install /usr/bin/java java /opt/jdk1.8.0_161/bin/java 1
sudo update-alternatives --install /usr/bin/javac javac /opt/jdk1.8.0_161/bin/javac 1
sudo update-alternatives --install /usr/bin/javaws javaws /opt/jdk1.8.0_161/bin/javaws 1

After executing update-alternatives its time to set JAVA_HOME. Open /etc/environment file and add following lines to it :

JAVA_HOME="/opt/jdk1.8.0_161/"
export JAVE_HOME

Then type

source /etc/environment

now in command line if you type

java -version

you will see the result.

What does $? mean in linux?

The $? is called exit code of an application used to run. As an example type following commands in console:
top
then press CTRL-Z and then type:
echo $?
You will see 147 as the answer. Return values more than 128 usually means signal and you have to do the calculation by subtracting 128 from the returned result. In this case you will get 19 which means SIGSTOP

You can read about linux signals using :

man 7 signal