Docker container – health check

HEALTHCHECK instruction is used to tell the Docker how to test a container to see if its still working or not.  This is used to detect cases such as a web server that is stuck in an infinite loop and unable to handle new connections, even though the server is still running.

In this post, I am going to show an example how we can add this instruction in the Dockerfile and test this.

Refer my code @java-rest-docker

Here is my Dockerfile


FROM java:8

WORKDIR /opt

ADD hello-rest-${project.version}-jar-with-dependencies.jar /opt/helloRest.jar
ADD bin /opt/bin

RUN chmod +x /opt/helloRest.jar
RUN chmod +x /opt/bin/run.sh

CMD ["/opt/bin/run.sh"]

EXPOSE 8080

HEALTHCHECK --interval=1m --timeout=3s CMD curl -f http://localhost:8080/api/greeting || exit 1

Check the last line of my Dockerfile where I instruct the docker to access localhost:8080/api/greeting in every 1 min and do the health check and fail it if there are any issues. If the exit status 0 when the container is healthy. If it’s 1, then it’s not in healthy status.

My docker CE version is 17.06.2-ce.

Now Build the image and the run the docker container.Then run this command and check the health check output. Here replace the container name “elegant_bartik” with your local container name.


  docker inspect --format "{{json .State.Health }}" elegant_bartik

Output:
{“Status”:”healthy”,”FailingStreak”:0,”Log”:[{“Start”:”2017-09-19T18:23:54.612256172-04:00″,”End”:”2017-09-19T18:23:54.652678939-04:00″,”ExitCode”:0,”Output”:” % Total % Received % Xferd Average Speed Time Time Time Current\n Dload Upload Total Spent Left Speed\n\r 0 0 0 0 0 0 0 0 –:–:– –:–:– –:–:– 0\r100 11 100 11 0 0 1022 0 –:–:– –:–:– –:–:– 1100\nHello World”},{“Start”:”2017-09-19T18:24:54.652833383-04:00″,”End”:”2017-09-19T18:24:54.696685407-04:00″,”ExitCode”:0,”Output”:” % Total % Received % Xferd Average Speed Time Time Time Current\n Dload Upload Total Spent Left Speed\n\r 0 0 0 0 0 0 0 0 –:–:– –:–:– –:–:– 0\r100 11 100 11 0 0 842 0 –:–:– –:–:– –:–:– 916\nHello World”},{“Start”:”2017-09-19T18:25:54.696867514-04:00″,”End”:”2017-09-19T18:25:54.748451647-04:00″,”ExitCode”:0,”Output”:” % Total % Received % Xferd Average Speed Time Time Time Current\n Dload Upload Total Spent Left Speed\n\r 0 0 0 0 0 0 0 0 –:–:– –:–:– –:–:– 0\r100 11 100 11 0 0 814 0 –:–:– –:–:– –:–:– 846\nHello World”},{“Start”:”2017-09-19T18:26:54.74862166-04:00″,”End”:”2017-09-19T18:26:54.798137953-04:00″,”ExitCode”:0,”Output”:” % Total % Received % Xferd Average Speed Time Time Time Current\n Dload Upload Total Spent Left Speed\n\r 0 0 0 0 0 0 0 0 –:–:– –:–:– –:–:– 0\r100 11 100 11 0 0 663 0 –:–:– –:–:– –:–:– 687\nHello World”},{“Start”:”2017-09-19T18:27:54.798446904-04:00″,”End”:”2017-09-19T18:27:54.845142125-04:00″,”ExitCode”:0,”Output”:” % Total % Received % Xferd Average Speed Time Time Time Current\n Dload Upload Total Spent Left Speed\n\r 0 0 0 0 0 0 0 0 –:–:– –:–:– –:–:– 0Hello World\r100 11 100 11 0 0 991 0 –:–:– –:–:– –:–:– 1100\n”}]}

Advertisements

JAX-RS – How to bind Filters to JAX-RS resources with Name Binding

In my earlier post, I have given an example to know how to bind the Filters to JAX-RS resources with Dynamic Binding.

Refer it @ JAX-RS Dynamic Binding

In this post, I am going to show how we can do that with Name Binding.

Follow the below steps to do that.

1. Create a custom annotation which uses @NameBinding annotation.


package com.resource;

import javax.ws.rs.NameBinding;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@NameBinding
@Target({ ElementType.METHOD, ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface AppKeyVerifier {}

2. Create a filter and implement the app key verification logic. Then apply the custom annotation to it.


package com.resource;

import com.model.AppError;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.Provider;
import java.io.IOException;

@Provider
@AppKeyVerifier
public class AppKeyVerifierFilter implements ContainerRequestFilter {

    private final static Logger LOGGER = LoggerFactory.getLogger(AppKeyVerifierFilter.class);

    private final static String APP_KEY = "994f4fe4-APP-KEY-f3d5af27e1ee";

    @Context
    private HttpServletRequest httpServletRequest;

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {

        String appKeyValueFromHeader = requestContext.getHeaderString("APP-KEY");
        LOGGER.info("APP Key:{}", appKeyValueFromHeader);
        if (!StringUtils.equals(APP_KEY, appKeyValueFromHeader) ) {
            requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED)
                                             .entity(new AppError("APP-KEY is not available or wrong"))
                                             .build());

        }
    }
}

3. Then apply the custom annotation to the target resource classes or methods.


package com.resource;

import com.model.User;
import com.service.UserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Inject;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import java.util.List;

@AppKeyVerifier
@Path("users")
public class UserResource {

    private final static Logger LOGGER = LoggerFactory.getLogger(UserResource.class);

    @Inject
    UserService userService;

    @GET
    @Path("get/{id}")
    @Produces(MediaType.APPLICATION_JSON)
    public User get(@PathParam("id") String id) throws Exception {
        LOGGER.debug("get method is called with id:{}", id);
        return userService.get(id);
    }
    @GET
    @Path("getAll")
    @Produces(MediaType.APPLICATION_JSON)
    public List getAll() throws Exception {
        LOGGER.debug("getAll method is called");
        return userService.getAll();
    }

    @POST
    @Path("add")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public String save(User user) throws Exception {
        LOGGER.debug("save method is called with user:{}", user);
        return userService.save(user);
    }

    @DELETE
    @Path("delete/{id}")
    @Produces(MediaType.TEXT_PLAIN)
    public String remove(@PathParam("id") String id) throws Exception {
        LOGGER.debug("remove method is called with id:{}", id);
        return userService.remove(id);
    }

    @POST
    @Path("update")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public String update(User user) throws Exception {
        LOGGER.debug("update method is called with user:{}", user);
        return userService.update(user);
    }
}

Refer my code @https://github.com/dkbalachandar/java-rest-docker

JAX-RS – How to bind Filters to JAX-RS resources with Dynamic Binding

JAX-RS provides multiple ways using which server side filters and interceptors can be bound to their target components.

By default, JAX-RS filters and interceptors are applied to all the methods of a resource class. This can be overridden with Named Binding or Dynamic binding. In this post, I am going to give an example to show how to do that.

Assume that we have a JAX-RS application which has two end points. They are “greeting” and “users”. Now we want to protect the “users” service from unauthorized access. So we want to intercept all the “users” service request and do some validation and then allow it if it’s valid. Assume that the client has to pass through an APP-KEY in the request header.

Here are my resource classes.

UserResource.java


package com.resource;

import com.model.User;
import com.service.UserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Inject;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import java.util.List;

@Path("users")
public class UserResource {

    private final static Logger LOGGER = LoggerFactory.getLogger(UserResource.class);

    @Inject
    UserService userService;

    @GET
    @Path("get/{id}")
    @Produces(MediaType.APPLICATION_JSON)
    public User get(@PathParam("id") String id) throws Exception {
        LOGGER.debug("get method is called with id:{}", id);
        return userService.get(id);
    }
    @GET
    @Path("getAll")
    @Produces(MediaType.APPLICATION_JSON)
    public List getAll() throws Exception {
        LOGGER.debug("getAll method is called");
        return userService.getAll();
    }

    @POST
    @Path("add")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public String save(User user) throws Exception {
        LOGGER.debug("save method is called with user:{}", user);
        return userService.save(user);
    }

    @DELETE
    @Path("delete/{id}")
    @Produces(MediaType.TEXT_PLAIN)
    public String remove(@PathParam("id") String id) throws Exception {
        LOGGER.debug("remove method is called with id:{}", id);
        return userService.remove(id);
    }

    @POST
    @Path("update")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public String update(User user) throws Exception {
        LOGGER.debug("update method is called with user:{}", user);
        return userService.update(user);
    }
}

HelloWorldResource.java



package com.resource;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

@Path("/greeting")
public class HelloWorldResource {

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public Response sayHello(){
        System.out.println("sayHello");
        return Response.ok().entity("Hello World").build();
    }
}

Dynamic binding

It provides a way to add/bind the filters to resource methods/classes programmatically.

We have to add a Feature class which implements the DynamicFeature interface. Then override the configure method and provide our own logic to bind the filers to resource methods.

In my code, I am checking the resource class name with the APP_CLASS_LIST list. If that list contains the class name, then binding the AppRequestFilter to that class. I don’t want to filter the greeting service. Hence the APP_CLASS_LIST contains only the UserResource class name alone.

Refer the below example.


package com.resource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.ws.rs.container.DynamicFeature;
import javax.ws.rs.container.ResourceInfo;
import javax.ws.rs.core.FeatureContext;
import javax.ws.rs.ext.Provider;
import java.util.Arrays;
import java.util.List;

@Provider
public class AppKeyFeature implements DynamicFeature {


    private final static Logger LOGGER = LoggerFactory.getLogger(AppKeyFeature.class);

    private static final List APP_CLASS_LIST = Arrays.asList(UserResource.class);

    @Override
    public void configure(ResourceInfo resourceInfo, FeatureContext context) {
        LOGGER.info("resourceInfo.getResourceClass():{}", resourceInfo.getResourceClass());
        if (APP_CLASS_LIST.contains(resourceInfo.getResourceClass())) {
            context.register(AppRequestFilter.class);
        }
    }
}

Refer below my filter code. This filter will validate the APP_KEY available in the request header with the default one. If it does not match, then it will abort the request and throw an error message. Make sure that you should not add @provider annotation for AppRequestFilter.


package com.resource;

import com.model.AppError;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import java.io.IOException;

public class AppRequestFilter implements ContainerRequestFilter {

    private final static Logger LOGGER = LoggerFactory.getLogger(AppRequestFilter.class);

    private final static String APP_KEY = "994f4fe4-APP-KEY-f3d5af27e1ee";

    @Context
    private HttpServletRequest httpServletRequest;

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {

        String appKeyValueFromHeader = requestContext.getHeaderString("APP-KEY");
        LOGGER.info("APP Key:{}", appKeyValueFromHeader);
        if (!StringUtils.equals(APP_KEY, appKeyValueFromHeader) ) {
            requestContext.abortWith(Response.status(Status.UNAUTHORIZED)
                                             .entity(new AppError("APP-KEY is not available or wrong"))
                                             .build());

        }
    }
}

Refer my code @https://github.com/dkbalachandar/java-rest-docker

Mockito Tutorial

Mockito is a testing framework and widely used now. Lets see how to use this in this tutorial.

I am going to use Maven + Java 8 + Mockito 2.0.2-beta version for this example.

Assume that we have a class called User Service which does save, get, remove and update operation on DB. So UserService class contains an instance of UserDao class which does all the CRUD operations. Here are those classes.

UserService.java


package com;

import java.util.UUID;

public class UserService {

    private UserDao userDao;

    public UserService(UserDao userDao) {
        this.userDao = userDao;
    }

    public User get(String id) {
        return userDao.get(id);
    }

    public String save(String firstName, String lastName, int age) throws Exception {
        User user = new User();
        user.setAge(age);
        user.setFirstName(firstName);
        user.setLastName(lastName);
        user.setId(UUID.randomUUID().toString());
        return userDao.save(user);
    }

    public String remove(String id) throws Exception {
        return userDao.remove(id);
    }

    public String update(String id, String firstName, String lastName, int age) throws Exception {
        User user = new User();
        user.setAge(age);
        user.setFirstName(firstName);
        user.setLastName(lastName);
        user.setId(id);
        return userDao.update(user);
    }
}

UserDao.java



package com;

public interface UserDao {
    public User get(String id);
    public String save(User user) throws Exception;
    public String remove(String id) throws Exception;
    public String update(User user) throws Exception;
}


UserDaoImpl.java
Its a dummy class only and actual implementation may be different


package com;

public class UserDaoImpl implements UserDao {

    @Override
    public User get(String id) {
        //Dummy implementation
        return null;
    }

    @Override
    public String save(User user) throws Exception {
        return "id";
    }

    @Override
    public String remove(String id) throws Exception {
       return id;
    }

    @Override
    public String update(User user) throws Exception {
        return "id";
    }
}


Now we want to mock the UserDao class and write the unit test cases for the UserService class. Here is my Junit test case


import com.User;
import com.UserDao;
import com.UserService;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;

import java.util.UUID;

import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.times;

public class UserServiceTest {

    UserDao userDao;

    User mockUser;

    UserService userService;

    @Before
    public void setUp() {
        userDao = Mockito.mock(UserDao.class);
        userService = new UserService(userDao);
        mockUser = new User();
        mockUser.setId(UUID.randomUUID().toString());
        mockUser.setFirstName("test");
        mockUser.setLastName("test");
        mockUser.setAge(40);
    }

    @Test
    public void testSave() throws Exception {
        //Using argument captor to test the values passed to the UserDao.save method
        ArgumentCaptor userArgumentCaptor = ArgumentCaptor.forClass(User.class);
        userService.save("bala", "samy", 35);
        Mockito.verify(userDao).save(userArgumentCaptor.capture());
        //Test the method called once
        Mockito.verify(userDao, times(1)).save(any());
        User storedUser = userArgumentCaptor.getValue();
        assertEquals("bala", storedUser.getFirstName());
        assertEquals("samy", storedUser.getLastName());
        assertEquals(35, storedUser.getAge());

    }

    @Test
    public void testGetUsingSpy() throws Exception {
        UserService userService = new UserService(userDao);
        UserService spy = Mockito.spy(userService);
        Mockito.when(spy.get(mockUser.getId())).thenReturn(mockUser);
        assertEquals(mockUser, spy.get(mockUser.getId()));
    }

    @Test
    public void testGetWithMock() throws Exception {
        Mockito.when(userDao.get(mockUser.getId())).thenReturn(mockUser);
        assertEquals(mockUser, userService.get(mockUser.getId()));
    }

    @Test
    public void testSaveWithMock() throws Exception {
        Mockito.when(userDao.save(any())).thenReturn(mockUser.getId());
        String value = userService.save(mockUser.getFirstName(), mockUser.getLastName(), mockUser.getAge());
        assertEquals(mockUser.getId(), value);
    }

    @Test
    public void testRemoveWithMock() throws Exception {
        Mockito.when(userDao.remove(any())).thenReturn(mockUser.getId());
        assertEquals(mockUser.getId(),userService.remove(mockUser.getId()));
    }

}


Argument Captor:

In the testSave method, I have used Argument Captor to capture the arguments that are getting passed to the UserDao.save method. Here we are passing the User Object to that method. So if we want to verify the async call or test the arguments to any class, then we could use Argument Captor.

In all the remaining test methods, I have mocked the methods of UserDao class.

Refer the code @https://github.com/dkbalachandar/mockitoExample

Java 8 – Builder pattern with Consumer interface

In this post, I am going to provide an example to show how we can implement the Builder pattern with Consumer interface.

With the use of Consumer interface, we can get rid of some boilerplate code in Java. Assume that you have a model class “Book” which contains a builder class inside of it. So to create an instance of “Book”, We can make use of this builder class. See the below example to know how we can do it in a normal way.


import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Book {

    private String title;
    private List authors;
    private float price;

    public Book(Builder builder) {
        this.title = builder.title;
        this.authors = builder.authors;
        this.price = builder.price;
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("Builder{");
        sb.append("title='").append(title).append('\'');
        sb.append(", authors=").append(authors);
        sb.append(", price=").append(price);
        sb.append('}');
        return sb.toString();
    }

    public static class Builder {

        private String title;
        private List authors;
        private float price;

        public Builder withTitle(String title){
            this.title=title;
            return this;
        }
        public Builder withAuthors(List authors){
            this.authors=authors;
            return this;
        }
        public Builder withPrice(float price){
            this.price=price;
            return this;
        }
        public Book build() {
            return new Book(this);
        }
    }

    public static void main(String[] args) {
        Book book = new Builder().withTitle("Design Patterns: Elements of Reusable Object-Oriented Software").
                withAuthors(new ArrayList(Arrays.asList(new String[]{"Erich Gamma", "John Vlissides", "Ralph Johnson", "Richard Helm"}))).
                withPrice(16.98f).build();
        System.out.println(book);
    }
}

If you look at the above example, then you can understand there are two many setter methods available in the Builder class. We can avoid this with Java 8 Consumer interface. I have changed the above example class with Consumer interface.

Refer below the code.


import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;

public class Book {

    private String title;
    private List authors;
    private float price;

    public Book(Builder builder) {
        this.title = builder.title;
        this.authors = builder.authors;
        this.price = builder.price;
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("Builder{");
        sb.append("title='").append(title).append('\'');
        sb.append(", authors=").append(authors);
        sb.append(", price=").append(price);
        sb.append('}');
        return sb.toString();
    }

    public static class Builder {

        public String title;
        private List authors;
        public float price;

        public Builder with(Consumer function) {
            function.accept(this);
            return this;
        }

        public Book build() {
            return new Book(this);
        }
    }

    public static void main(String[] args) {
        Book book = new Book.Builder().with(bookData -> {
            bookData.title = "Design Patterns: Elements of Reusable Object-Oriented Software";
            bookData.authors = new ArrayList(
                    Arrays.asList(new String[]{"Erich Gamma", "John Vlissides", "Ralph Johnson", "Richard Helm"}));
            bookData.price = 16.98f;
        }).build();
        System.out.println(book);
    }
}

In the above code, I have removed all the setter methods available in the Builder class and created only one method with() which takes the Consumer function as input.

Consumer is a functional interface in Java and it accepts a single input argument and does not return any value. Here we are going to pass a Consumer object with a value for each field(Builder class).

We can also update the reference name ‘bookData’ with any symbol. Refer the below example where I have used the symbol ‘$’ to make it simpler.


  public static void main(String[] args) {
        Book book = new Book.Builder().with($ -> {
            $.title = "Design Patterns: Elements of Reusable Object-Oriented Software";
            $.authors = new ArrayList(
                    Arrays.asList(new String[]{"Erich Gamma", "John Vlissides", "Ralph Johnson", "Richard Helm"}));
            $.price = 16.98f;
        }).build();
        System.out.println(book);
    } 

The one drawback of this approach is to update all the fields of the Builder class as public. But this should be fine as we are not going to modify the actual class (Book) here.

Java 8 – Optional class

Java 8 contains a new class called ‘Optional’ in the utils package which is used to represent a value is present or not.

This class is used to avoid the Null pointer exception. So we don’t need to do any null check in our code if we use Optional object along with our object.

Refer the below example to know how we can use the Optional class.


import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

public class OptionalMain {

    public static void main(String[] args) {

        OptionalMain optionalMain = new OptionalMain();
        optionalMain.testBasic();
        optionalMain.testFilter();
        optionalMain.testMap();
    }

    private void testBasic() {

        // Create the Optional object with a string object. Note the string is null.
        //This will create Optional.empty as the value is null
        Optional stringOptional = Optional.ofNullable(null);
        System.out.println("stringOptional:" + stringOptional);
        try {
            //If you pass null value to of method, then it will throw Null pointer exception
            System.out.println("ofNullable on Non-Empty Optional: " + Optional.of(null));
        }
        catch (NullPointerException npx) {
            npx.printStackTrace();
        }

        // Check whether the value is present in the Optional object
        System.out.println("stringOptional value is present: " + stringOptional.isPresent());

        // Get the value from the Optional object. If its null, then return the default value
        // passed to the orElse method
        System.out.println("stringOptional value " + stringOptional.orElse("EMPTY"));

        // Create the Optional object with a string object.
        Optional nameOptional = Optional.ofNullable("name");
        System.out.println("nameOptional name is present:" + nameOptional.isPresent());
        nameOptional.ifPresent(name -> System.out.println("Name is available"));

        try {
            // Get the value from the Optional object. If its null, then throw an exception
            System.out.println("stringOptional value " + stringOptional.orElseThrow(RuntimeException::new));
        }
        catch (RuntimeException ex) {
            ex.printStackTrace();
        }
    }

    private void testFilter() {
        Optional nameOptional = Optional.of("john");
        System.out.println("Filter Operation:" + nameOptional.filter(name -> name.equals("john")));
        Optional numOptional = Optional.of(100);
        System.out.println(numOptional.filter(value -> value > 10));
        System.out.println(numOptional.filter(value -> value > 100));
    }

    private void testMap() {

        List fruits = Arrays.asList("apple", "orange", "banana");
        Optional<List> fruitsOptional = Optional.ofNullable(fruits);
        System.out.println("Fruits" + fruitsOptional.get());
        System.out.println("Mapping the Fruits " + fruitsOptional.get().stream().map(String::toUpperCase)
                .collect(Collectors.toList()));

    }
}

Output:


stringOptional:Optional.empty
stringOptional value is present: false
stringOptional value EMPTY
nameOptional name is present:true
java.lang.NullPointerException
	at java.util.Objects.requireNonNull(Objects.java:203)
	at java.util.Optional.(Optional.java:96)
	at java.util.Optional.of(Optional.java:108)	
Name is available
java.lang.RuntimeException
	at java.util.Optional.orElseThrow(Optional.java:290)
Filter Operation:Optional[john]
Optional[100]
Optional.empty
Fruits[apple, orange, banana]
Mapping the Fruits [APPLE, ORANGE, BANANA]


Stackshare

If you want to know about the tools or application stack used in any major tech company, then you can visit this web site stackshare 

This is a crowd sourcing site and it acts as a software discovery platform and lets us search for the best software tools and which companies are using them.