9 Mistakes to Avoid While Programming in Java
What mistakes should be avoided while programming in Java? In the following piece we answers this question.
Is there a golden mean to handle many environments for a great number on just one machine? Our Java Expert Bartłomiej knows the answer!
Let’s take a look at a typical work environment in a software house. You have a few customers that have different environments. Some prefer MySQL, others prefer Postgres. One version of your application needs Java 11, and another Java 17. Frontend needs npm 12 or 16 because you use different versions of angular. Finally, you have that three-dimensional array that contains combinations of all your DBs, backend, and frontend versions. Sounds bad, but one day your boss says…
The situation described above isn’t something uncommon. Migration between language or framework versions, updates of databases or simply different requirements coming from customers could turn upside-down all configurations. We should have a solution that helps us manage those changes, one that matches a few assumptions and/or requirements and/or goals:
In this article, I will focus on three areas. First is tools for Java and JVM. The second is general-purpose tools. The third is how to use docker to achieve our goals.
When you are a Java developer or, more generally, you work with JVM technologies, then you can use SDKMAN!. This is a very nice and easy-to-use tool that supports many libraries, frameworks, and languages.
The installation process of SDKMAN! Is pretty simple. You need to run:
curl -s "https://get.sdkman.io" | bash
and then
source "$HOME/.sdkman/bin/sdkman-init.sh"
Now you can manage your Java, Scala and Maven versions.
In this example, we will install and update the version of a few tools. This is just a small subset of available tools.
Let’s assume your new project uses Java 17. You don’t have any Java version installed. You want to install it and, in addition, add a Maven Daemon tool to make the builds faster. So, you need to install Maven, too. To do that, you need to execute three simple commands:
$ sdk install java 17-open
$ sdk install maven 3.8.4
$ sdk install mvnd 0.7.1
At the end of installing each tool, you will be asked about making it default:
Do you want Java 17-open to be set as default? (Y/n):
This is important when you install a new version of a library or a language, because of SDKMAN! will set that default version as global for all terminals for the current user.
From time to time, SDKMAN! needs to update indexes. During this, you could a get message that there are some new versions of tools that you use. We can check which versions are available by typing sdk ls <TOOL>
. For sdk ls maven
:
Available Maven Versions
================================================================================
3.8.6 3.3.3
3.8.5 3.3.1
3.8.4 3.2.5
3.8.3 3.2.3
3.8.2 3.2.2
3.8.1 3.2.1
3.6.3 3.1.1
3.6.2 3.1.0
3.6.1 3.0.5
3.6.0 3.0.4
3.5.4
3.5.3
3.5.2
3.5.0
3.3.9
================================================================================
local version
currently in use
================================================================================
As we see above, Maven has a newer version than the one we use. The same is for mvnd
(0.8.2) and Java (19-open). Let’s update everything. To do that, we just need to call the install command but in this time, we don’t use a version specifier:
$ sdk install maven
$ sdk install mvnd
$ sdk install java
But something wrong happened. Maven
and mvnd
have correct versions, but Java has version 17.0.5-tem
. That’s because “the newest” version of the tool is controlled by its vendor not local SDKMAN! who is this vendor? A vendor in SDKMAN! is someone who can publish a version. However, let’s say that we finally install 19-open
, and we made it default, but for some reason, we need to use 17-open
.
We can configure a default
version of a tool that is global for all projects and terminals. But when we need a specific version, we have two ways to do that. First is to use the sdk use <TOOL> <VERSION>
command every time when we want to use a specific version of a tool in the current terminal. Second is to prepare a versions list in a .sdkmanrc
file that is stored with the project.
While the first option is for single use, the second is designed for working with teams and sharing configurations between team members.
SDKMAN! is very easy to use and has a rich library of supported tools, frameworks and languages. It works on Linux, MacOS and Windows. On the other hand, this tool is JVM-focused and requires the author’s acceptance to be a vendor. In addition, the mechanic of .sdkmanrc
is very poor and could significantly slow down the process of changing directories.
If you need to use many languages and tools, you should take a look at asdf. This tool is focused on “high-level” tools. While in SDKMAN! you can find many Java-specific tools, like Bpipe or Znai, asdf offers much more tools but not so specific. Of course, some of those tools overlap, e.g. Java, Tomcat or mvnd are available in both.
When you would like to use asdf
, you need to have git
and curl
installed. After that, you just:
git clone https://github.com/asdf-vm/asdf.git ~/.asdf --branch v0.10.2
and add these lines in ~/.bashrc
file:
. $HOME/.asdf/asdf.sh
. $HOME/.asdf/completions/asdf.bash
Now you can install plugins and tools in your favorite versions.
Unlike SDKMAN!, asdf
uses plugins to manage tools. So, before you can install a tool, you need to install a plugin. Let’s go back to our example project and try to configure the environment using asadfsdf
.
First, we need to install plugins:
asdf plugin add java
asdf plugin add maven
asdf plugin add mvnd
Then we can install our tools:
asdf install java openjdk-17
asdf install maven 3.8.4
asdf install mvnd 0.7.1
And once again, unlike SDKMAN!, asdf
doesn’t change anything in our environment. When we try to use java, we get an error message like:
No version is set for command Java
Consider adding one of the following versions in your config file at ~/.tool-versions
java openjdk-17
We need to create file .tool-versions
in the home directory to manage default versions.
Updating software versions with asdf
is pretty simple. We just install a new version. Because this process does not affect the environment, we could do that at any time and at any place in the file system. When we want to use a specific version of some software, we need to create in the project directory a .tool-versions
file that could be shared between team members. Remember that you need to guarantee that all team members have asdf
and plugins installed. The list of plugins you can find here.
asdf
supports not only programming languages, frameworks, and tools like vim or kubernetess. It supports databases, too. In such a case, we could install multiple versions of e.g. Postgres but only one instance could run. That’s because asdf
executes commands directly on your OS without any separation layer. That leads to problems like “port already in use” and conflicts on resources.
asdf
is a very good tool if you would like to work in a polyglot environment. It supports many tools, languages, and frameworks. Plugin-based architecture makes it very easy to extend that. However, some of the tools that it has in the library need additional work during installation to provide all required dependencies. asdf
does not work on Windows, even on WSL.
When I talk about the port conflict above, many of you know the solution.
Docker could help us in some cases. I mention it out of duty, because this tool is so big and complex that we cannot discuss it in one article.
Together with Docker, we should use a docker-compose tool that gives us the possibility to coordinate multi-container environments.
Docker helps us manage tools that need some specific resources, like ports or files. It separates instances in containers and gives us full control over them. Nevertheless, Docker is a tool that introduces a lot of complexity to our working environment and could be problematic in some cases. One of those cases of using Docker in a test is described in one of our previous article.
I know that I didn’t describe all tools that could be used for managing tool versions. There are many more of them, such as jEnv that could replace SDKMAN,
or NVM that we can use to manage npm or RVM for Ruby. I focused on tools that I “tested on the battlefield” and can recommend to anyone.