Category Archives: Java

How to change default Java version on Ubuntu Linux if we have multiple versions of JDK?

It is common for us to have multiple versions of Java/JDK in our Ubuntu Linux namely Java 8, 11 or 17…but how can we change the default Java/JDK version? We can use the update-alternatives command inside the Ubuntu.

First thing first, first we must make sure we have multiple versions of JDK inside our Ubuntu machine, we can check using the command below:

apt list --installed *jdk*

Alternatively, you can use update-alternatives command too to list out all Java inside your machine.

update-alternatives --list java

After confirmed we have multiple versions of Java, we can run the update-alternatives --config command. For e.g. to update Java version, we use the command below:

sudo update-alternatives --config java

It will list out options for you to choose like below. The * beside 1 mean current choice, and you have 0 to 3 options to choose. For this example, 0 had been selected. The update-alternatives will then update the symbolic links determining default Java command.

There are 3 choices for the alternative java (providing /usr/bin/java).

  Selection    Path                                            Priority   Status
------------------------------------------------------------
  0            /usr/lib/jvm/java-17-openjdk-amd64/bin/java      1711      auto mode
* 1            /usr/lib/jvm/java-11-openjdk-amd64/bin/java      1111      manual mode
  2            /usr/lib/jvm/java-17-openjdk-amd64/bin/java      1711      manual mode
  3            /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java   1081      manual mode

Press <enter> to keep the current choice[*], or type selection number: 0
update-alternatives: using /usr/lib/jvm/java-17-openjdk-amd64/bin/java to provide /usr/bin/java (java) in auto mode

Please note the command above only update java command. You still have other commands that need to update accordingly. You can find most of the commands in /usr/lib/jvm/java-17-openjdk-amd64/bin/ folder. For e.g. in this scenario, the folder has the command below:

ls /usr/lib/jvm/java-17-openjdk-amd64/bin/
jar        javac    jcmd      jdeprscan  jhsdb   jlink  jpackage    jshell  jstatd       serialver
jarsigner  javadoc  jconsole  jdeps      jimage  jmap   jps         jstack  keytool
java       javap    jdb       jfr        jinfo   jmod   jrunscript  jstat   rmiregistry

You might do not need to update them all since it depends on your usage, but it is quite common to update java, jar, javac, javadoc. The commands will be like below:

sudo update-alternatives --config jar
sudo update-alternatives --config javac
sudo update-alternatives --config javadoc

Raspberry Pi Building and Running a Java App by Using Maven and Terminal

To build and run Java applications on Raspberry Pi, we need to install Java JDK (Java Development Kit) and Maven. As the Java Development Kit (JDK) name suggested, JDK provides you with the tool to do Java development. Meanwhile, Maven is a build management tool for Java that is central to project build tasks such as download dependency from a remote repository, compilation, packaging, and artifact management. Without Maven, you need to manually download the dependencies, create files and folders, and run a lot of tedious commands to do similar things that Maven can provide you conveniently.

To install Java JDK and Maven on Raspberry Pi, please use the commands below:

sudo apt update
sudo apt install default-jdk
sudo apt install maven

After you have installed, run the commands below to verify Java and Maven are available on your Pi. Please note Maven command is mvn.

java --version
mvn --version

You should be getting something like below:

$ java --version
openjdk 11.0.16 2022-07-19
OpenJDK Runtime Environment (build 11.0.16+8-post-Debian-1deb10u1)
OpenJDK 64-Bit Server VM (build 11.0.16+8-post-Debian-1deb10u1, mixed mode)

$ mvn --version
Apache Maven 3.6.0
Maven home: /usr/share/maven
Java version: 11.0.16, vendor: Debian, runtime: /usr/lib/jvm/java-11-openjdk-arm64
Default locale: en_US, platform encoding: UTF-8
OS name: "linux", version: "5.10.103-v8+", arch: "aarch64", family: "unix"

Run the command below to download a quickstart archetype for Maven

mvn org.apache.maven.plugins:maven-archetype-plugin:3.1.2:generate -DarchetypeArtifactId="maven-archetype-quickstart" -DarchetypeGroupId="org.apache.maven.archetypes" -DarchetypeVersion="1.4" -DgroupId="com.example" -DartifactId="mvndemo"

After it has downloaded, switch to the mvndemo directory. You can now run the command below to build and package the Java app

mvn clean package

If the build succeeds, you should be to see something like the below:

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  10.615 s
[INFO] Finished at: 2023-01-08T22:46:00+08:00
[INFO] ------------------------------------------------------------------------

To run the Java app, use the command below:

cd target
java -cp mvndemo-1.0-SNAPSHOT.jar com.example.App

Please note, if you don’t want to specify com.example.App every time, you can set the main class for the Jar in pom.xml. Use any terminal editor such as GNU Nano to edit the pom.xml. Search the maven-jar-plugin under plugin.

        <plugin>
          <artifactId>maven-jar-plugin</artifactId>
          <version>3.0.2</version>
        </plugin>

Then, add the main class for the Jar file.

        <plugin>
          <artifactId>maven-jar-plugin</artifactId>
          <version>3.0.2</version>
          <configuration>
            <archive>
              <manifest>
                <mainClass>com.example.App</mainClass>
              </manifest>
            </archive>
          </configuration>
        </plugin>

Finally, we run the commands below to package and run the Java app

mvn clean package
cd target
java -jar mvndemo-1.0-SNAPSHOT.jar 

ERR_TOO_MANY_REDIRECTS OKTA Authentication in Spring Boot

You are trying to develop an OKTA login authentication in Java Spring Boot, but you get ERR_TOO_MANY_REDIRECTS error as like below:

This page isn’t working right now
localhost redirected you too many times.
To fix this issue, try clearing your cookies.
ERR_TOO_MANY_REDIRECTS

How to resolve this issue? First to check if this is due to Sign-in redirect URIs or not, we can try set the URI to default listening URI for OKTA code http://localhost:8080/login/oauth2/code/okta. Go to OKTA Admin Dashboard site > Applications > Applications and change the URI to default listening URI

Change the application.properties redirect URI to as below:

okta.oauth2.redirect-uri=/login/oauth2/code/okta

Run the Spring Boot application, if you can login successfully, then it should be some configuration or code issue.

We change back the redirect URI to original URI

okta.oauth2.redirect-uri=/authorization-code/callback

From the OKTA documentation, found this needs to be added:

package com.example.oktademo;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
class OktaOAuth2WebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // all routes protected
        http.authorizeRequests()
                .anyRequest().authenticated()
                // enable OAuth2/OIDC
                .and()
                .oauth2Login();
    }
}

Run the Spring Boot app again, you should be able to login without error.

Reference:
https://www.baeldung.com/spring-security-okta
https://developer.okta.com/docs/guides/protect-your-api/springboot/main/#require-authorization-for-everything
https://devforum.okta.com/t/setting-up-okta-oidc-for-use-with-spring-boot-2-3-1-release/14490
https://stackoverflow.com/questions/58110915/too-many-redirects-okta-authentication

Consuming a RESTful Web Service in Spring Boot using RestTemplate

How to consume a RESTful Web Service in Spring Boot? If you have checked spring.io, they have a guide on how to use it but unfortunately, the API is unable to access during I tried the example. Therefore, this blog post will use an alternative quote API using a similar approach.

This tutorial will build an application by using Spring’s RestTemplate to retrieve a random quote from https://api.quotable.io/random. We will use Visual Studio Code as our text editor, JDK 1.8 or later, and Maven. Please make sure Java has been setup correctly, then Extension Pack for Java and Spring Initializr Java Support extensions for VS code have been installed too (the publisher of the extensions is Microsoft).

Press Ctrl+Alt+P to run the VS Code Command Palette:

  1. Type Spring Initializr and then select Spring Initializr: Create a Maven Project…
  2. Specify Spring Boot version, normally default will do
  3. Specify project language: Java
  4. Group Id: com.example.consumingrest
  5. Artifact Id: ConsumingRest
  6. Packaging type: Jar
  7. Java version: 8+
  8. Dependencies: Spring Web
  9. Lastly, select a destination folder you want to project file extract to.

We will get the RESTful service by using https://api.quotable.io/random and it can be browsed using a web browser. The JSON document will look something like this:

{
    "_id": "w0fjvau7GwbH",
    "tags": [
        "famous-quotes"
    ],
    "content": "All is flux; nothing stays still.",
    "author": "Heraclitus",
    "authorSlug": "heraclitus",
    "length": 33,
    "dateAdded": "2019-10-12",
    "dateModified": "2019-10-12"
}

We will create a domain class (src/main/java/com/example/consumingrest/Quote.java) that contains the fields for the JSON object.

package com.example.consumingrest;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;

@JsonIgnoreProperties(ignoreUnknown = true)
public class Quote {
    @JsonProperty("_id")
    private String _id;
    @JsonProperty("content")
    private String content;
    @JsonProperty("author")
    private String author;
    @JsonProperty("authorSlug")
    private String authorSlug;
    @JsonProperty("length")
    private int length;
    @JsonProperty("tags")
    private String[] tags;

    public Quote() {
    }

    public String get_Id() {
        return _id;
    }

    public void set_Id(String _id) {
        this._id = _id;
    }

    public String get_Content() {
        return content;
    }

    public void set_Content(String content) {
        this.content = content;
    }

    public String get_Author() {
        return author;
    }

    public void set_Author(String author) {
        this.author = author;
    }

    public String get_AuthorSlug() {
        return authorSlug;
    }

    public void set_AuthorSlug(String authorSlug) {
        this.authorSlug = authorSlug;
    }

    public int get_Length() {
        return length;
    }

    public void set_Length(int length) {
        this.length = length;
    }

    public String[] get_Tags() {
        return tags;
    }

    public void set_Tags(String[] tags) {
        this.tags = tags;
    }

    @Override
    public String toString() {
        return "@Value {" +
            "_id=" + _id +
            ", content=" + content +
            ", author=" + author +
            ", authorSlug=" + authorSlug +
            ", length=" + length +
            ", tags=" + tagsToString() + '\'' +
            "}";
    }

    private String tagsToString() {
        StringBuilder sb = new StringBuilder();
        for (String string : tags) {
            sb.append(string + ", ");
        }
        return sb.substring(0, sb.length() - 2);
    }
}

Then, we will use a RestTemplate as a synchronized web client to perform requests to the API and it will use the Jackson JSON processing library to process the API Data.

// src/main/java/com/example/consumingrest/ConsumingRestApplication.java
package com.example.consumingrest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
public class ConsumingRestApplication {
	// Logger to output result to console
	private static final Logger log = LoggerFactory.getLogger(ConsumingRestApplication.class);

	public static void main(String[] args) {
		SpringApplication.run(ConsumingRestApplication.class, args);
	}

	@Bean
	public RestTemplate restTemplate(RestTemplateBuilder builder) {
		return builder.build();
	}

	@Bean
	public CommandLineRunner run(RestTemplate restTemplate) throws Exception { // Runs the RestTemplate
		return args -> {
			Quote quote = restTemplate.getForObject("https://api.quotable.io/random", Quote.class);
			log.info(quote.toString());
		};
	}

}

Finally, just hit Run Java / Debug Java using the Run or Debug button on the top right of VS Code. Alternatively, you can use this command ./mvnw spring-boot:run to execute it without debug function.

The sample result is as below:

2022-02-13 12:07:54.979  INFO 11731 --- [           main] c.e.c.ConsumingRestApplication           : No active profile set, falling back to default profiles: default
2022-02-13 12:07:57.873  INFO 11731 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2022-02-13 12:07:57.908  INFO 11731 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2022-02-13 12:07:57.909  INFO 11731 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.56]
2022-02-13 12:07:58.130  INFO 11731 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2022-02-13 12:07:58.131  INFO 11731 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 2958 ms
2022-02-13 12:07:59.317  INFO 11731 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2022-02-13 12:07:59.349  INFO 11731 --- [           main] c.e.c.ConsumingRestApplication           : Started ConsumingRestApplication in 5.661 seconds (JVM running for 6.724)
2022-02-13 12:08:01.327  INFO 11731 --- [           main] c.e.c.ConsumingRestApplication           : @Value {_id=eDIMF-N3ATJ3, content=Let your hook always be cast; in the pool where you least expect it, there will be a fish., author=Ovid, authorSlug=ovid, length=90, tags=famous-quotes'}

Reference:
https://spring.io/guides/gs/consuming-rest/
https://github.com/lukePeavey/quotable#get-random-quote

Sample project repository:
https://github.com/sanme98/gs-consuming-rest

What is Eureka Service Discovery in Java and .NET?

Do you ever hear about Netflix Eureka? Do you clear what are the purposes of Eureka and Service Discovery? If you go to check in the Netflix Eureka Github, below are the introduction:

Eureka is a RESTful (Representational State Transfer) service that is primarily used in the AWS cloud for the purpose of discovery, load balancing and failover of middle-tier servers. It plays a critical role in Netflix mid-tier infra.

But how about if we are still not clear about Eureka and Service Discovery after we have read the introduction.

Actually, in layman terms, Eureka is something similar to a phonebook. For e.g., we want to dial a person named Joe, but we don’t know the number, so we take the phonebook and check the Joe number. After we got the number, then we can dial Joe correctly.

In IT terms, Eureka is something similar to DNS (Domain Name Server). For e.g., we want to connect to wordpress.com, but we don’t know where it is actually located in which IP, so we send the inquiry to DNS, then it will return the IP address for wordpress.com, with the IP, we can connect to wordpress.com correctly.

Hopefully, with the above two metaphors, we will have clearer ideas about Eureka Service Discovery.

Quick Start Java 11+ Development using Visual Studio Code

To develop Java 11+ apps using Visual Studio Code, first, we need to download Java Extension Pack for VS Code. Proceed to VS Code Extensions (Ctrl+Shift+X), search for Java Extension Pack, then install it and reload the VS Code.

After that, open the Command Palette (Ctrl+Shift+P), and type Java: Configure Java Runtime, open the command, and you will see the page which can configure Java Development Kit.

This is the page to download the OpenJDK, or you can use the Oracle JDK which can be download from Oracle website. My scenario is I downloaded it from Oracle and installed. After you installed and re-open VS Code, the page should be able to recognize the JDK that you installed (refer to item 4 in the screenshot above). If can’t, you can configure it using Windows System Environment Variable > Path > JAVA_HOME (item 3 in the screenshot above). Then, we can start our first Java development.

After you open your VS Code in a new folder, open the Explorer (Ctrl+Shift+E), and add a new file call Hello.java. Copy in the code below and then hit the Run button as per highlighted below. The Hello World! displayed in the popup Terminal.

public class Hello {
    public static void main(String[] args){
        System.out.println("Hello World!");
    }
}

From the steps above, we can see it is quite simple to use the VS Code for Java development, we have nothing much need to set up and basically just install and start the development only.

FAQ:

Q: Why we use Java 11 for VS Code?

A: Current version of VS Code only support Java 11 and above. You still can develop and compile Java 1.8+ (which can configure via “java.configuration.runtimes” in settings), but the JDK for VS Code must be 11+.

Q: Is VS Code support Maven Build and Create the project?

A: Yes, I will show it in the next blog post and you have just installed the Maven extension via Java Extension Pack just now. Meanwhile, please refer to Maven in 5 Minutes.

Q: Is VS Code Java supports Java Applet?

A: Strictly said, No. It is because Java Applet has been deprecated. Luckily, though is deprecated, you still can do some development using Applet classes since it still available inside Oracle Java 13 (others I not sure as I not yet installed to check). But most of the time, people just use it for legacy applications and the experience will be different. For e.g. Oracle is no longer included Applet Viewer in Java 13.