Showing posts with label Dev Tools. Show all posts
Showing posts with label Dev Tools. Show all posts
Some time back I have written one article Java Lambda Expression Explained with Example but it was easy for me to explore and Java 8 because I was using it in my project and I was allowed to install and use it. But in my current project, we are still using Java 8 and now I want to upgrade myself and learn Java 11 but can not install it.

And this happens with a lot of Java developers, Sometimes you want to learn and explore the latest version of Java e.g. Java 11 but you can not install it on your machine because you and your team is working on some older version e.g. Java 8, and you do not want to break your project.

Or suppose you are working on multiple projects some of which use Java 8 and some new projects are using Java 11. So in order to work on these projects parallelly, you will need to install multiple JDK on your machine and you should be able to switch between them.

What if there is a way and what if you are enabled to install multiple versions of Java and turn off and turn on them according to your need.




Well there is a tool called SDKMan which allow us to do the same and according to the official website:

SDKMan! is a tool for managing parallel versions of multiple Software Development Kits on most Unix based systems. It provides a convenient Command Line Interface (CLI) and API for installing, switching, removing and listing Candidates.

Some points about SDKMan are as following:
  1. SDKMan is free to use and it is developed by the open-source community.
  2. SDKMan is written in bash and it only requires curl and zip/unzip programs to be present on your system.
  3. SDKMan can install around 29 Software Development Kits for the JVM such as Java, Groovy, Scala, Kotlin and Ceylon. Ant, Gradle, Grails, Maven, SBT, Spark, Spring Boot, Vert.x.
  4. We do not need to worry about setting the _HOME and PATH environment variables because SDKMan handles it automatically.

Installing SDKMan

SDKMan can run on any UNIX based platforms such as Mac OSX, Linux, Cygwin, Solaris and FreeBSD and we can install it using following commands:

Simply open a new terminal and enter:

$ curl -s "https://get.sdkman.io" | bash
$ source "$HOME/.sdkman/bin/sdkman-init.sh"

We can verify the installation using sdk version and sdk help command will us complete help about the usage of sdk command.

Because SDKMan is written in bash and only requires curl and zip/unzip to be present on your system. You can install SDKMan on windows as well either by first installing Cygwin or Git Bash for Windows environment and then running above commands.

Installing Java Using SDKMan

SDKMan supports installations of 29 Software Development Kits for the JVM e.g. Java, Groovy, Scala, Kotlin and Ceylon, Grails, SBT, Spark, Spring Boot. We can get the complete list using sdk list command.

SDKMan also helps us install build tools such as Ant, Maven and Gradle, You can read more about these build tools on Java Build Tools Comparisons: Ant vs Maven vs Gradle.

Command sdk list java will give us a list of java versions which we can install using SDKMan like below:

$ sdk list java

================================================================================
Available Java Versions
================================================================================
     13.ea.02-open       1.0.0-rc-10-grl                                        
     12.ea.26-open       1.0.0-rc-9-grl                                         
     11.0.2-zulu         1.0.0-rc-8-grl                                         
     11.0.2-open                                                                
     11.0.1-zulufx                                                              
     10.0.2-zulu                                                                
     10.0.2-open                                                                
     9.0.7-zulu                                                                 
     9.0.4-open                                                                 
     8.0.202-amzn                                                               
     8.0.201-zulu                                                               
     8.0.201-oracle                                                             
     8.0.192-zulufx                                                             
     7.0.181-zulu                                                               
     1.0.0-rc-11-grl                                                            
================================================================================
+ - local version
* - installed
> - currently in use
================================================================================

Now Suppose we want to install OpenJDK version of Java v9 then we will execute below command in our terminal which will also update the PATH and JAVA_HOME variables:

$ sdk install java 9.0.4-open

The command will take some time to execute because it will be downloading the JDK on our machine and the command will output something like:

Downloading: java 9.0.4-open
In progress...
######################################################################## 100.0%

Repackaging Java 9.0.4-open...

Done repackaging...
Cleaning up residual files...

Installing: java 9.0.4-open
Done installing!

Setting java 9.0.4-open as default.


Now if we check our Java version and JAVA_HOME, we can see Java has been updated to 9.0.4

$ java -version
openjdk version "9.0.4"
OpenJDK Runtime Environment (build 9.0.4+11)
OpenJDK 64-Bit Server VM (build 9.0.4+11, mixed mode)

$ echo $JAVA_HOME
/Users/xbbnrde/.sdkman/candidates/java/current

We can follow the same process to install any version which we can see in the above list, let us suppose we want to install Java V11 version then we can execute the command:

$ sdk install java 11.0.2-open
Downloading: java 11.0.2-open
In progress...
######################################################################## 100.0%

Repackaging Java 11.0.2-open...

Done repackaging...
Cleaning up residual files...

Installing: java 11.0.2-open
Done installing!

Do you want java 11.0.2-open to be set as default? (Y/n): Y

Setting java 11.0.2-open as default.

Now if we again check our Java version and JAVA_HOME, we can see Java has been updated to 11.0.2:

$ java -version
openjdk version "11.0.2" 2019-01-15
OpenJDK Runtime Environment 18.9 (build 11.0.2+9)
OpenJDK 64-Bit Server VM 18.9 (build 11.0.2+9, mixed mode)

$ echo $JAVA_HOME
/Users/xbbnrde/.sdkman/candidates/java/current

Note: Some times you will require to close and reopen your terminal in order to see the changes.
sdkman-installing-and-running-java11-and-java-9-simultaneously

Pointing SDKMan to your existing installed Java version

First, we need to find out where on your machine Java is installed. On my machine, it is installed in the folder jdk1.8.0_172.jdk which lies under the folder /Library/Java/JavaVirtualMachines, let's refer to this folder by <java-folder>.

The second thing we want to do is to set up a symbolic link between our installed <java-folder> and SDKMan, we can do it by running below commands:

$ ln -s /Library/Java/JavaVirtualMachines/<java-folder>  ~/.sdkman/candidates/java/<java-folder>

$ sudo ln -s /Library/Java/JavaVirtualMachin
es/<java-folder>/Contents/Home/bin  ~/.sdkman/candidates/java/<java-folder>/bin


Now if we again execute sdk list java command, we will get:

================================================================================
Available Java Versions
================================================================================
   + jdk1.8.0_172.jd     8.0.201-zulu                                           
     13.ea.02-open       8.0.201-oracle                                         
     12.ea.26-open       8.0.192-zulufx                                         
     11.0.2-zulu         7.0.181-zulu                                           
 > * 11.0.2-open         1.0.0-rc-12-grl                                        
     11.0.2.j9-adpt      1.0.0-rc-11-grl                                        
     11.0.2.hs-adpt      1.0.0-rc-10-grl                                        
     11.0.1-zulufx       1.0.0-rc-9-grl                                         
     10.0.2-zulu         1.0.0-rc-8-grl                                         
     10.0.2-open                                                                
     9.0.7-zulu                                                                 
   * 9.0.4-open                                                                 
     8.0.202-amzn                                                               
     8.0.202.j9-adpt                                                            
     8.0.202.hs-adpt                                                            

================================================================================
+ - local version
* - installed
> - currently in use
================================================================================


We can clearly see that we have 3 JDK installed on our machine and JDK 11.0.2-open is in use as of now. However, if we want to switch between them or install a new one we can do that very easily by using sdk use java <version_which_you_want_to_use> like the following:

Using Java 9

$ sdk use java 9.0.4-open
Using java version 9.0.4-open in this shell.

$ java -version
openjdk version "9.0.4"
OpenJDK Runtime Environment (build 9.0.4+11)
OpenJDK 64-Bit Server VM (build 9.0.4+11, mixed mode)

Using locally installed Java 8

$ sdk use java jdk1.8.0_172.jdk
Using java version jdk1.8.0_172.jdk in this shell.
Add caption
$ java -version java version "1.8.0_172" Java(TM) SE Runtime Environment (build 1.8.0_172-b11) Java HotSpot(TM) 64-Bit Server VM (build 25.172-b11, mixed mode)

Using Java 11

$ sdk use java 11.0.2-open
Using java version 11.0.2-open in this shell.

$ java -version
openjdk version "11.0.2" 2019-01-15
OpenJDK Runtime Environment 18.9 (build 11.0.2+9)
OpenJDK 64-Bit Server VM 18.9 (build 11.0.2+9, mixed mode)


Using sdk use command will only enable you to use a specified version of JDK in the same terminal shell where you are executing the sdk use command. And if you close the terminal and open it again you will be on the previously installed version.

But if you want to activate one version of JDK for all terminals and applications, you can use the command sdk default java <your_version>

$ sdk default java 11.0.2-open
Default java version set to 11.0.2-open

$ java -version
openjdk version "11.0.2" 2019-01-15
OpenJDK Runtime Environment 18.9 (build 11.0.2+9)
OpenJDK 64-Bit Server VM 18.9 (build 11.0.2+9, mixed mode)
Or you can uninstall that version and install it again like below which will set that version to the current JDK.

Uninstalling a JDK Version

In case you want to uninstall any JDK version e.g., 11.0.2-open you can do that as follows:

$ sdk uninstall java 11.0.2-open
And in case you want to install the uninstalled version back again, you can install it again and this time SDKMan will not download it because it already has that on your machine (unless you do not delete that manually).

$ sdk install java 11.0.2-open

Found a previously downloaded java 11.0.2-open archive. Not downloading it again...

Installing: java 11.0.2-open
Done installing!

Do you want java 11.0.2-open to be set as default? (Y/n): y

Setting java 11.0.2-open as default.

Using SDMan installed JDK from IntelliJ IDEA

SDKMan installs all JDK under .sdkman/candidates/java/ folder and you can find .sdkman folder in your home directory.

After opening any Java project in IntelliJ, you can press Command + : to open the project structure window. In that window, you will need to click on New... under Project SDK section and there you can enter .sdkman/candidates/java/ in the browse window and can select any JDK from .sdkman/candidates/java/.

Because .sdkman is a hidden folder and in case you face some problem in finding it, you can always create a symbolic link to it with a non-hidden folder using below command.

$ ln -s ~/.sdkman ~/sdkman

You can find the complete source code for my articles on this Github Repository and please feel free to provide your valuable feedback.

Git is a most widely used and powerful version control system for tracking changes in computer files and coordinating work on those files among multiple people. It is primarily used for source code management in software development, but it can be used to keep track of changes in any set of files.

Git was developed by Linus Torvalds in 2005 as a distributed open-source software version control software and of course, it is free to use. As a distributed revision control system it is aimed at speed, data integrity, and support for distributed, non-linear workflows.

While other version control systems e.g. CVS, SVN keeps most of their data like commit logs on a central server, every git repository on every computer is a full-fledged repository with a complete history and full version tracking abilities, independent of network access or a central server.

However, almost all IDEs support git out of the box and we do not require submitting the git commands manually but it is always good to understand these commands. Below is a list of some git commands to work efficiently with git.

Git Help

The most useful command in git is git help which provides us with all the help we require. If we type git help in the terminal, we will get:

useful-git-commands

Command git help -a will give us a complete list of git commands:
Available git commands in '/usr/local/git/libexec/git-core'
  add                     gc                      receive-pack
  add--interactive        get-tar-commit-id       reflog
  am                      grep                    remote
  annotate                gui                     remote-ext
  apply                   gui--askpass            remote-fd
  archimport              gui--askyesno           remote-ftp
  archive                 gui.tcl                 remote-ftps
  askpass                 hash-object             remote-http
  bisect                  help                    remote-https
  bisect--helper          http-backend            repack
  blame                   http-fetch              replace
  branch                  http-push               request-pull
  bundle                  imap-send               rerere
  cat-file                index-pack              reset
  check-attr              init                    rev-list
  check-ignore            init-db                 rev-parse
  check-mailmap           instaweb                revert
  check-ref-format        interpret-trailers      rm
  checkout                log                     send-email
  checkout-index          ls-files                send-pack
  cherry                  ls-remote               sh-i18n--envsubst
  cherry-pick             ls-tree                 shortlog
  citool                  mailinfo                show
  clean                   mailsplit               show-branch
  clone                   merge                   show-index
  column                  merge-base              show-ref
  commit                  merge-file              stage
  commit-tree             merge-index             stash
  config                  merge-octopus           status
  count-objects           merge-one-file          stripspace
  credential              merge-ours              submodule
  credential-manager      merge-recursive         submodule--helper
  credential-store        merge-resolve           subtree
  credential-wincred      merge-subtree           svn
  cvsexportcommit         merge-tree              symbolic-ref
  cvsimport               mergetool               tag
  daemon                  mktag                   unpack-file
  describe                mktree                  unpack-objects
  diff                    mv                      update
  diff-files              name-rev                update-git-for-windows
  diff-index              notes                   update-index
  diff-tree               p4                      update-ref
  difftool                pack-objects            update-server-info
  difftool--helper        pack-redundant          upload-archive
  fast-export             pack-refs               upload-pack
  fast-import             patch-id                var
  fetch                   prune                   verify-commit
  fetch-pack              prune-packed            verify-pack
  filter-branch           pull                    verify-tag
  fmt-merge-msg           push                    web--browse
  for-each-ref            quiltimport             whatchanged
  format-patch            read-tree               worktree
  fsck                    rebase                  write-tree
  fsck-objects            rebase--helper

And command git help -g will give us a list of git concepts which git think is good for us:
The common Git guides are:

   attributes   Defining attributes per path
   everyday     Everyday Git With 20 Commands Or So
   glossary     A Git glossary
   ignore       Specifies intentionally untracked files to ignore
   modules      Defining submodule properties
   revisions    Specifying revisions and ranges for Git
   tutorial     A tutorial introduction to Git (for version 1.5.1 or newer)
   workflows    An overview of recommended workflows with Git

We can use git help <command> or git help <concept> command to know more about a specific command or concept.

Git Configuration



DescriptionGit Command
List all configurations applied to the current repository in order (picked from bottom to top).git config --list Or git config --global --list 
Configure the author name to be used with your commits by replacing old entries.git config --global --replace-all user.name "Sam Smith"
Configure the author email address to be used with your commitsgit config --global user.email sam@example.com
Will remove user credential details from the repositorygit config --local credential.helper ""
List all currently configured remote repository URLsgit remote -v
If you haven't connected your local repository to a remote server, To add a remote server to a local repositorygit remote add origin <repo_url>


Git Commit and Push



DescriptionGit Command
Create a file name README.md with Readme content contentecho "Readme content" >> README.md
List the files you've changed and those you still need to add or commitgit status
Add all or one file to the staging areagit add . OR git add folder/file_name
Remove all or one file from the staging area  git reset HEAD OR git reset HEAD folder/file_name
Commit changes to head with a messagegit commit -m 'message'
Commit any files you've added with git add, and also commit any files you've changed since thengit commit -a
Send all commits from local repository to remote repositorygit push
Do a git push and sets the default remote branch for the current local branch. So any future git pull command will attempt to bring in commits from the <remote-branch>into the current local branchgit push -u <remote-branch>
Send changes to the master branch of your remote repositorygit push origin master
Push a specific branch to your remote repositorygit push origin <branch_name>
Push all branches to your remote repositorygit push --all origin


Git Checkout And Pull



DescriptionGit Command
To create a new local repositorygit init
Clone a repository into a new directorygit clone repo_url
Clone a repository into a new directory and point to mentioned branch_namegit clone --branch branch_name repo_url
To create a working copy of a local repositorygit clone /path/to/repository
Download objects and refs from a remote repository for the master branchgit fetch origin master
To merge a different branch into your active branchgit merge <branch_name>
Fetch and merge changes on the remote server to your working directory:git pull
View all the merge conflicts, View the conflicts against the base file, Preview changes, before merginggit diff, git diff --base <filename>, git diff <sourcebranch> <targetbranch>


Git Branch



DescriptionGit Command
List all the branches in your repository, and also tell you what branch you're currently ingit branch
Switch from one branch to anothergit checkout branch_name
Create a new branch and switch to itgit checkout -b branch_name
Create a new branch from the master branch and switch to itgit checkout -b branch_name master
Delete the feature branch from the local repositorygit branch -d <branch_name>
Delete a branch on your remote repositorygit push origin :<branch_name>


Git Cleaning



DescriptionGit Command
Fetch the latest history (objects & refs) from the remote server for the master branchgit fetch origin master
Clean the repository to the last push git clean -x -d -f
Reset local repository and point your local master branch to latest history fetched from the remote servergit reset --hard origin/master
To bring all changes from a remote repository to the local repositorygit pull origin master


Other Git commands



DescriptionGit Command
You can use tagging to mark a significant changeset, such as a releasegit tag 1.0.0 <commitID>
Commit Id is the leading characters of the changeset ID, up to 10, but must be unique. Get the ID usinggit log
Push all tags to the remote repositorygit push --tags origin
If you mess up, you can replace the changes in your working tree with the last content in head: Changes already added to the index, as well as new files, will be keptgit checkout -- <filename>
Search the working directory for foo()git grep "foo()"

I keep these commands as notes for my future reference and you can find more on this Github Repository and please feel free to provide your valuable feedback.
Lombok is a tool that generates code like getters, setters, constructors, equals, hashCode, toString for us in the same way that our IDE does. While IDE generates all these things in our source code file, Lombok generates them directly in the class file.

So Lombok basically takes out all these things from your source code to bytecode so we don't need to write them in our source code which means less code in our source code file. And in this article, I am going to explain how Lombok can help us in removing this kind of boilerplate code.

To understand it let's suppose we have an entity class Employee and we want to use it to hold a single employee record. We can use it as a DTO or persistent entity or anything else we want but idea is that we want to use it to store id, firstName, lastName and salary fields.

For this requirement, we will need a simple Employee POJO and according to General directions for creating Plain Old Java Object,
  • Each variable in a POJO should be declared as private.
  • Default constructor should be overridden with public accessibility.
  • Each variable should have its Setter-Getter method with public accessibility.
  • POJO should override equals(), hashCode() and toString() methods of Object.
And generally our Employee class will look like

public class Employee {
  private long id;
  private int salary;
  private String firstName;
  private String lastName;

  public Employee() {
  }

  public long getId() {
    return id;
  }
  public void setId(long id) {
    this.id = id;
  }
  public int getSalary() {
    return salary;
  }
  public void setSalary(int salary) {
    this.salary = salary;
  }
  public String getFirstName() {
    return firstName;
  }
  public void setFirstName(String firstName) {
    this.firstName = firstName;
  }
  public String getLastName() {
    return lastName;
  }
  public void setLastName(String lastName) {
    this.lastName = lastName;
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;

    Employee employee = (Employee) o;

    if (id != employee.id) return false;
    if (salary != employee.salary) return false;
    if (!firstName.equals(employee.firstName)) return false;
    if (!lastName.equals(employee.lastName)) return false;

    return true;
  }

  @Override
  public int hashCode() {
    int result = (int) (id ^ (id >>> 32));
    result = 31 * result + firstName.hashCode();
    result = 31 * result + lastName.hashCode();
    result = 31 * result + salary;
    return result;
  }

  @Override
  public String toString() {
    return "Employee{" +
           "id=" + id +
           ", firstName='" + firstName + '\'' +
           ", lastName='" + lastName + '\'' +
           ", salary=" + salary +
          '}';
  }
}

But generally, we always use auto-generation strategies of our IDE to generate getters, setters, default constructor, hashCode, equals and toString e.g. alt+insert in IntelliJ.

As you can see the size of Employee class is more than 50 lines where field declaration is contributing only 4 lines. And these things are not directly contributing anything to our business logic but just increasing the size of our code.

Project Lombok provides a way to remove above boilerplate code and simplify development process while still providing these functionalities at the bytecode level. With project Lombok, we can combine all these things within 10 lines

@Data
public class Employee {
  private long id;
  private int salary;
  private String firstName;
  private String lastName;
}

With @Data annotation on top of our class Lombok will process our Java source code and produce a class file which will have getters, setters, default arg constructor, hasCode, equals and toString methods in it. So basically Lombok is doing the trick and instead of us adding all those things in our source code and then compiling it a class file Lombok is automatically adding all these things directly to our class files.

But if we need to write some business code in our getters or setters or in any of above method or we want trick these methods to function a little bit differently, we can still write that method in our class and Lombok will not override it while producing all these stuff in bytecode.

In order to make it works, we need to
  1. Install Lombok plugin in our IDE e.g. In IntelliJ we can install it from Settings -> Plugins -> Browse Repositories window.installing-lombok-plugin-in-intellij-idea
  2. Enable annotation processing e.g. In IntelliJ we need to check “Enable annotation processing” option on Settings -> Compiler -> Annotation Processors window.enabling-annotation-in-intellij-idea
  3. Include Lombok jar in our build path, we can do it by adding the Lombok dependency in pom.xml file if we are using maven or we will need to download the Lombok jar manually and add it to our classpath.
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.16.12</version>
      <optional>true</optional>
    <dependency>
    
Lombok provides a variety of annotations which we can use and manipulate according to our need. Some of these annotations are
  • @NonNull Can be used with fields, methods, parameters, and local variables to check for NullPointerException.
  • @Cleanup Provides automatic resource management and ensures the variable declaration that you annotate will be cleaned up by calling its close method. Seems similar to Java’s try-with-resource.
    @Cleanup InputStream in = new FileInputStream("filename");
    
  • @Getter/@Setter Can be used on class or field to generate getters and setters automatically for every field inside the class or for a particular field respectively.
    @Getter @Setter private long id;
    
  • @ToString Generates a default toString method
  • @EqualsAndHashCode Generates hashCode and equals implementations from the fields of your object.
    @ToString(exclude = "salary")
    @EqualsAndHashCode(exclude = "salary")
    
  • @NoArgsConstructor , @RequiredArgsConstructor and @AllArgsConstructor Generates constructors that take no arguments, one argument per final / non-null field, or one argument for every field.
  • @Data A shortcut for @ToString , @EqualsAndHashCode , @Getter on all fields, and @Setter on all non-final fields, and @RequiredArgsConstructor .
  • @Value is the immutable variant of @Data, Helps in making our class Immutable.
  • @Builder annotation will generate nice static factory methods in our class which we can use to create objects of our class in more oriented manner e.g. if we will add “@Builder” annotation to our Employee class then we can create object of Employee in the following manner
    Employee emp = Employee.builder()
                           .firstName("Naresh")
                           .lastName("Joshi")
                           .build();
    
  • @SneakyThrows Allows us to throw checked exceptions without actually declaring this in our method’s throws clause, e.g.
    @SneakyThrows(Exception.class)
    public void doSomeThing() {
      // According to some business condition throw some business exception
      throw new Exception();
    }
    
  • @Synchronized A safer variant of the synchronized method modifier.
  • @CommonsLog, @JBossLog, @Log, @Log4j, @Log4j2, @Slf4j and @XSlf4j which produces log fields in our class and let us use that field for logging. e.g. If we will mark a class with @CommonsLog Lombok will attach below field to our class.
    private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog(YourClass.class);
    
You can also go to the official website of project Lombok for the complete feature list and examples.

Advantages of Lombok

  • Lombok helps us remove boilerplate code and decrease line of unnecessary code
  • It makes our code highly maintainable, we don’t need to worry about regenerating hashCode, equals, toString, getters, and setters whenever we change our properties.
  • Lombok provides an efficient builder API to build our object by using @Builder
  • Lombok provides efficient way to make our class Immutable by using @Value
  • Provides other annotations like @Log - for logging, @Cleanup - for cleaning resources automatically, @SneakyThrows  - for throwing checked exception without adding try-catch or throws statement and @Synchronized to make our methods synchronized.

Disadvantages of Lombok

The only disadvantage Lombok comes with is its dependency, If you are using it then everyone in your project must use it and configure it (install the plugin and enable annotation processing) to successfully compile the project. And all your project mates need to be aware of it otherwise, they will not be able to build the project and receive lots of compilation errors. However, this is only an initial step and will not take more than a couple of minutes.
Previous Post Older Posts Home