Categories
Java

Deploy to Tomcat

Deploy myApp.war file to Tomcat application server, using Tomcat manager app and an ant script.

Make a folder "ant" in your project and this it the folder for all other actions below.

Make a folder "dist" and generate your myApp.war file into this folder with the "build.xml" script.

Make a folder "tomcat-libs" and copy following files from your tomcat installation:

  • catalina-ant.jar
  • catalina.jar
  • servlet-api.jar
  • jsp-api.jar

Create file "tomcat.properties" with properties for tomcat server and tomcat manager app.

Create "deploy.xml" file for deployment.

Execute deploy.xml script with ant, it must show the info sections with properties of your tomcat.properties file.

Execute deploy-webapp target from deploy.xml script to deploy your myApp.war file to tomcat server through the tomcat manager app.

If you are using for example Jenkins, you can set the "secprops.location" from outside the script, so you can use the same script for different tomcat installations.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE project>
<project name="myApp" default="info" basedir=".">

  <description>
    myApp ant deploy file.
  </description>

  <property name="secprops.location" value="tomcat.properties" />
  <property file="${secprops.location}" />

  <property name="war.file" value="dist/myApp.war" />

  <target name="info">
    <echo message="-------------------------------------------------------------------" />
    <echo message="Commands:" />
    <echo message="  start-webapp" />
    <echo message="  stop-webapp" />
    <echo message="  undeploy-webapp" />
    <echo message="  deploy-webapp" />
    <echo message="  sessions-webapp" />
    <echo message="-------------------------------------------------------------------" />
    <echo message="Properties: "/>
    <echo message="tomcat.manager.url = ${tomcat.manager.url}"/>
    <echo message="tomcat.username    = ${tomcat.username}"/>
    <echo message="tomcat.password    = ${tomcat.password}"/>
    <echo message="webapp.name        = ${webapp.name}"/>
    <echo message="war.file           = ${war.file}"/>
    <echo message="-------------------------------------------------------------------" />
  </target>

  <path id="catalina-ant-classpath">
    <fileset dir="tomcat-libs">
      <include name="catalina-ant.jar" />
      <include name="catalina.jar" />
    </fileset>
  </path>

  <taskdef name="catalina-start" classname="org.apache.catalina.ant.StartTask" classpathref="catalina-ant-classpath" />
  <taskdef name="catalina-stop" classname="org.apache.catalina.ant.StopTask" classpathref="catalina-ant-classpath" />
  <taskdef name="catalina-deploy" classname="org.apache.catalina.ant.DeployTask" classpathref="catalina-ant-classpath" />
  <taskdef name="catalina-undeploy" classname="org.apache.catalina.ant.UndeployTask" classpathref="catalina-ant-classpath" />
  <taskdef name="catalina-sessions" classname="org.apache.catalina.ant.SessionsTask" classpathref="catalina-ant-classpath" />
  
  <target name="sessions-webapp">
    <catalina-sessions url="${tomcat.manager.url}" username="${tomcat.username}" password="${tomcat.password}" path="/${webapp.name}" failonerror="false" />
  </target>
  
  
  <target name="stop-webapp">
    <catalina-stop url="${tomcat.manager.url}" username="${tomcat.username}" password="${tomcat.password}" path="/${webapp.name}" failonerror="false" />
  </target>

  <target name="start-webapp">
    <catalina-start url="${tomcat.manager.url}" username="${tomcat.username}" password="${tomcat.password}" path="/${webapp.name}" />
  </target>

  <target name="undeploy-webapp">
    <catalina-undeploy url="${tomcat.manager.url}" username="${tomcat.username}" password="${tomcat.password}" path="/${webapp.name}" failonerror="false" />
  </target>

  <target name="deploy-webapp">
    <echo message="START to deploy file ${war.file} to Tomcat Server: ${tomcat.manager.url}" />
    <catalina-deploy url="${tomcat.manager.url}" username="${tomcat.username}" password="${tomcat.password}" path="/${webapp.name}" war="file:${war.file}" update="true"/>
    <echo message="END of deployment" />
  </target>

</project>
tomcat.manager.url=http://myAppServer:7011/manager/text
tomcat.username=tomcat
tomcat.password=tomcat
webapp.name=myApp
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE project>
<project name="myApp" default="clean" basedir=".">
  <description>
    myApp ant build file.
  </description>
  <!-- Set global properties for this build -->
  <property name="src" location="../source"/>
  <property name="webcontent" location="../WebContent"/>
  <property name="build" location="build"/>

  <tstamp prefix="build-info">
    <format property="current-date" pattern="d-MMMM-yyyy" locale="en" />
    <format property="current-time" pattern="hh:mm:ss a z" locale="en" />
  </tstamp>

  <target name="init">
    <!-- Create the build directory structure used by compile -->
    <mkdir dir="${build}"/>
    <!-- Create the distribution directory -->
    <mkdir dir="${dist}/lib"/>
  </target>

  <target name="compile" depends="init" description="compile the source">
    <!-- Compile the Java code from ${src} into ${build} -->
    <javac srcdir="${src}" destdir="${build}" includeantruntime="false" encoding="UTF-8">
      <classpath>
        <!-- Taken from Apache Tomcat 8.5 lib folder -->
        <pathelement path="tomcat-libs/servlet-api.jar"/>
        <pathelement path="tomcat-libs/jsp-api.jar"/>
      </classpath>
    </javac>
    <!-- Copy all non-java ressoures from source folder -->
    <copy todir="${build}">
      <fileset dir="${src}" excludes="**/*.java" />
    </copy>

  </target>

  <target name="war" depends="compile">
    <war destfile="dist/myApp.war" webxml="${webcontent}/WEB-INF/web.xml">
      <fileset dir="${webcontent}"/>
      <classes dir="${build}"/>
      <manifest>
        <attribute name="Manifest-Version" value="1.0"/>
        <attribute name="Built-On" value="${build-info.current-date}"/>
        <attribute name="Built-At" value="${build-info.current-time}"/>
      </manifest>
    </war>
  </target>

  <target name="clean" description="clean up">
    <!-- Delete the ${build} directory trees -->
    <delete dir="${build}"/>
  </target>
</project>
Categories
Java Linux

Setup Tomcat manager app

In my last post I set up a Tomcat application server in general, now I enable Tomcat manager app for deployment.

# Tomcat Users
mv /app/myApp/tomcat/conf/tomcat-users.xml /app/myApp/tomcat/conf/tomcat-users.xml_original
vim /app/myApp/tomcat/conf/tomcat-users.xml

vim/app/myApp/tomcat/conf/server.xml

# By default the Manager is only accessible from a browser running on the same machine as Tomcat. If you wish to modify this restriction, you'll need to edit the Manager's context.xml file.
vim /app/myApp/tomcat/webapps/manager/META-INF/context.xml
<?xml version="1.0" encoding="UTF-8"?>
<tomcat-users xmlns="http://tomcat.apache.org/xml"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="http://tomcat.apache.org/xml tomcat-users.xsd"
              version="1.0">
 
  <role rolename="manager-gui"/>
  <role rolename="manager-script"/>
  <user username="tomcat" password="tomcat" roles="manager-gui,manager-script"/>
 
</tomcat-users>
<Server port="7010" shutdown="SHUTDOWN">
  <GlobalNamingResources>
    <Resource name="UserDatabase" auth="Container" type="org.apache.catalina.UserDatabase" description="User database that can be updated and saved" factory="org.apache.catalina.users.MemoryUserDatabaseFactory" pathname="conf/tomcat-users.xml" />
  </GlobalNamingResources>
 
  <Service name="Catalina">
    <Connector port="7011" />
   
    <!-- Define an AJP 1.3 Connector on port 7012 -->
    <Connector port="7012" protocol="AJP/1.3" secretRequired="false" />
    <Engine name="Catalina" defaultHost="localhost" jvmRoute="myApp-dev">
      <Host name="localhost" appBase="webapps" />
      <Realm className="org.apache.catalina.realm.LockOutRealm">
        <!-- This Realm uses the UserDatabase configured in the global JNDI
             resources under the key "UserDatabase".  Any edits
             that are performed against this UserDatabase are immediately
             available for use by the Realm.  -->
        <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
               resourceName="UserDatabase"/>
      </Realm>
    </Engine>
  </Service>
</Server>
<Context antiResourceLocking="false" privileged="true" >
    <!-- ## uncomment this Block ##
    <Valve className="org.apache.catalina.valves.RemoteAddrValve"
         allow="127\.\d+\.\d+\.\d+|::1|0:0:0:0:0:0:0:1" />
    -->
</Context>

Test Tomcat manager app

Open http://myAppServer:7011/manager and login with username: tomcat and password: tomcat.

Categories
Java Linux

Setup Tomcat application server

Setup files

su myUser
 
# Java
cd /app/java
tar -xzf /app/files/OpenJDK8U-jre_x64_linux_hotspot_8u265b01.tar.gz
tar -xzf /app/files/OpenJDK11U-jre_x64_linux_hotspot_11.0.8_10.tar.gz
tar -xzf /app/files/OpenJDK15U-jre_x64_linux_hotspot_15_36.tar.gz
 
# myApp Tomcat
cd /app/myApp
tar -xzf /app/files/apache-tomcat-8.5.59.tar.gz
tar -xzf /app/files/apache-tomcat-9.0.39.tar.gz
 
# Certificate
cp /app/files/corporate_truststore.jks /app/certs/
 
# SAP JCO
cp /app/files/_sapjco3-64/3.0.19/linuxx86/libsapjco3.so /app/library/
cp /app/files/_sapjco3-64/3.0.19/linuxx86/sapjco3.jar /app/library/

Setup Tomcat

su myUser
 
# Symlink to actual Tomcat version
ln -s /app/ccp/apache-tomcat-8.5.59 /app/myApp/tomcat
 
# remove sample application
# but keep the Tomcat Manager app for deployment
rm -rf /app/myApp/tomcat/webapps/docs
rm -rf /app/myApp/tomcat/webapps/examples
rm -rf /app/myApp/tomcat/webapps/ROOT
 
# configure Tomcat
vim /app/myApp/tomcat/bin/setenv.sh
mv /app/myApp/tomcat/conf/server.xml /app/myApp/tomcat/conf/server.xml_original
vim /app/myApp/tomcat/conf/server.xml
 
# expand Classpath
vim /app/myApp/tomcat/conf/catalina.properties
# tomcat/bin/setenv.sh
CATALINA_HOME=/app/myApp/tomcat
CATALINA_BASE=/app/myApp/tomcat
CATALINA_PID=/app/myApp/tomcat/tomcat.pid
JAVA_HOME=/app/java/jdk8u265-b01-jre
CATALINA_OPTS="$CATALINA_OPTS -Djava.library.path=/app/library"
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/app/library
export LD_LIBRARY_PATH
JAVA_OPTS="${JAVA_OPTS} -Djavax.net.ssl.trustStore=/app/certs/corporate_truststore.jks -Djavax.net.ssl.trustStorePassword=notchangeit -Xms512M -Xmx2048M -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/app/myApp/dump"

# tomcat/conf/catalina.properties
common.loader= [...] ,"/app/library/sapjco3.jar"
# server.xml
<Server port="7010" shutdown="SHUTDOWN">
  <Service name="Catalina">
    <Connector port="7011" />
   
    <!-- Define an AJP 1.3 Connector on port 7012 -->
    <Connector port="7012" protocol="AJP/1.3" secretRequired="false" />
    <Engine name="Catalina" defaultHost="localhost" jvmRoute="myApp-dev">
      <Host name="localhost" appBase="webapps" />
    </Engine>
  </Service>
</Server>

Tomcat as a Service

# As root
vim /etc/systemd/system/tomcat.service
# enable script:
systemctl enable tomcat.service
# tomcat.service

# Systemd unit file for myApp tomcat
#
# To create clones of this service:
   
# Systemd unit file for tomcat
 [Unit]
 Description=myApp Tomcat Web Application Container
 After=syslog.target network.target
   
   
 [Service]
 Type=forking
   
 ExecStart=/app/myApp/tomcat/bin/startup.sh
 ExecStop=/app/myApp/tomcat/bin/shutdown.sh
   
 User=myUser
 Group=myUser
   
 [Install]
 WantedBy=multi-user.target

Service control

Enable user myUser to control Tomcat services:

visudo -f /etc/sudoers
##################################################
## Allow user myUser to control (apache & tomcat) services
%%myUser ALL=(root) NOPASSWD: /bin/systemctl
%%myUser ALL=(root) NOPASSWD: /usr/sbin/service

Test Tomcat

For Tomcat testing I use my ShowHeaders app (GitHub).
ShowHeaders is a minimalistic webapp that is not much more than a "Hello World", but it shows the HTTP headers, what is quite useful when testing reverse proxy integration.

# copy ShowHeaders App for Testing (also for Reverse Proxy Configuration Testing)
[myUser@DEV ~]$ cp /app/files/ShowHeaders/ROOT.war /app/myApp/tomcat/webapps/
# start tomcat:
[myUser@DEV ~]$ sudo systemctl start tomcat

#
curl localhost:7011

Test connection from outside the server itself: http://myAppServer:7011/

Categories
Linux

Copy files with rsync

I have to setup and configure a DEV server and afterwards the QA/staging and PROD servers.

I will download required software with my laptop and upload to DEV server into folder /app/files. For other servers I just want to copy this folder from the DEV server.

Setup server

DEV

# install software
yum install rsync -y

# create user
adduser myUser

# create folder
mkdir /app/files
chown myUser:myUser /app/files

# switch to myUser
su myUser

# Create Private & Public keys
ssh-keygen -t rsa -b 4096            # generates id_rsa & id_rsa.pub in  /home/myUser/.ssh/

# authorize yourself
cat /home/myUser/.ssh/id_rsa.pub >> /home/myUser/.ssh/authorized_keys
chmod 0600 ~/.ssh/authorized_keys

QA / others

nearly the same setup, but we copy the keys from the DEV user, as he is the same as on the other servers.

# install software
yum install rsync -y

# create user
adduser myUser

# create folder
mkdir /app/files
chown myUser:myUser /app/files

# switch to myUser
su myUser

# create .ssh folder
mkdir ~/.ssh
chmod 0700 ~/.ssh

# create files, copy content from DEV server!
vi ~/.ssh/id_rsa
chmod 0600 ~/.ssh/id_rsa
vi ~/.ssh/id_rsa.pub
chmod 0600 ~/.ssh/id_rsa.pub
vi ~/.ssh/authorized_keys
chmod 0600 ~/.ssh/authorized_keys

Test SSH login

# login from DEV to QA
[myUser@DEV] ssh QA
# login from QA to DEV
[myUser@QA] ssh DEV

# loggin with explicit port
[myUser@DEV] ssh QA -p 22

Download software

Copy all files to /app/files

Java

Open URL https://adoptopenjdk.net/

Click on "Andere Plattformen"

Select for each:

  • JVM: HotSpot
  • Operating System: Linux
  • Architecture: x64

Java Versions:

  • OpenJDK 8 (LTS)
  • OpenJDK 11 (LTS)
  • OpenJDK 15 (Latest)

Download the JRE files.

Tomcat

Open URL: http://tomcat.apache.org/

Download Binary Distribution → Core → tar.gz-Version of:

  • Tomcat 8.5
  • Tomcat 9

SAP JCO

Copy the latest version from the corporate repository.

Path: _sapjco3-64/versions/3.0.19

Corporate Root CA

Copy the password ("notchangeit") protected truststore: corporate_truststore.jks

Copy from server to server

[myUser@QA ~]$ rsync --progress -avhe 'ssh -p 22' myUser@DEV:/app/files/ /app/files
Categories
Linux

Logical Volume Creation

In one of my current projects I have to attach a (virtual) disk to a (cloud) server.

Gather information

To get an overview of the system I used this commands:

pvs
pvdisplay

vgs
vgdisplay

lvs
lvdisplay

fdisk -c -l /dev/sdb

Interpretation of information

Physical VolumePartitionVolume GroupLogical VolumesSize
/dev/sda107 GB
/dev/sda/dev/sda10,5 GB
/dev/sda/dev/sda2106,5 GB
/dev/sda/dev/sda2vg00<99.50g
/dev/sda/dev/sda2vg00lv_home2 GB
/dev/sda/dev/sda2vg00lv_usr2 GB
/dev/sda/dev/sda2vg00"free"<76.50g
/dev/sdb107 GB

Technically it is possible to create a new logical volume inside volume group vg00, because there are > 76,5 GB unassigned and free storage.

But the vg00 is reserved for the Operating System and should not used for data from application.

Therefore the additional disk is needed for applicatin data.

Volume creation

Physical Volume with ~ 100 GB is attached to the server and added as /dev/sdb.

A volume group "vg_app" will be created with two logical volumes. One logical volume for logfiles, named "lv_logs" with 20 GB and the other with remaining free space, named "lv_app", for application data.

# create volume group
vgcreate -s 32M vg_app /dev/sdb

# create logical volume for logfiles
lvcreate -L 20G -n lv_log vg_app

# create logical volume for application data
lvcreate -l 100%%VG -n lv_app vg_app

# show information
vgdisplay -v /dev/vg_app

mount -a
Physical VolumeVolume GroupLogical VolumeSize
/dev/sdb107 GB
/dev/sdbvg_app107 GB
/dev/sdbvg_applv_log20 GB
/dev/sdbvg_applv_app> 79 GB

Create File system

mkfs -t ext4 /dev/vg_app/lv_app
mkfs -t ext4 /dev/vg_app/lv_log

Mount logical volumes

mkdir /app
mkdir /app/log
 
mount /dev/mapper/vg_app-lv_log /app/log
mount /dev/mapper/vg_app-lv_app /app
 
mount -a
vi /etc/fstab
mount -a
# /etc/fstab
# [...] existing files code [...]
/dev/mapper/vg_app-lv_app /app     ext4 defaults 0 0
/dev/mapper/vg_app-lv_log /app/log ext4 defaults 0 0

Test

Reboot and doublecheck that mounted volumes are still there.

reboot now
##########
df -h
Filesystem                 Size  Used Avail Use%% Mounted on
[...]
/dev/mapper/vg_app-lv_app   79G   57M   75G   1%% /app
/dev/mapper/vg_app-lv_log   20G   45M   19G   1%% /app/log

-> Both logical volumes are mounted --> Success!

Categories
Java

Git & SSL

In my first post about Git I wrote about the problem with non-public CA signed certitificates and how to handle it. I did not mention the easierst (and unsecured) way to handle this, so I write this post to have all possibilities in one place.

Add certificate to truststore

I download the public certificate of the CA from webbrowser and add it to the truststore of Git.

Where is the cert store of git?

git config --system --list
http.sslcainfo={PathToGit}/mingw64/ssl/certs/ca-bundle.crt

To add the non-public CA cert to Git cert store just open ca-bundle.crt and the downloaded certificate with an text editor and copy the content of the certificate to the ca-bundle.

Use Windows Networking Layer

I configured the sslBackend to the Windows Networking Layer:

# use SChannel, the built-in Windows networking layer. This means that it will use the Windows certificate storage mechanism and you do not need to explicitly configure the curl CA storage mechanism.
git config --global http.sslBackend schannel

Disable SSL Verify

The easierst and unsecure way is to simply disable SSL validation:

git config --global http.sslVerify false

This also works with the system configuration (--system instead of --global). I did this an a project with a very short time budget, we had to configure Git on the Linux system and this Git installation was used by a Jenkins. Both servers, Git & Jenkins, are in the same corporate intranet.

Categories
AWS

Example React application on AWS Amplify

I followed the steps through this tutorial: https://aws.amazon.com/de/getting-started/hands-on/build-react-app-amplify-graphql/

I is mostly well documented. But I had some obstacles:

Build specification

I had to add the backend part to the build specification:

version: 1
backend:
  phases:
    build:
      commands:
        - '# Execute Amplify CLI with the helper script'
        - amplifyPush --simple
frontend:
  phases:
    preBuild:
      commands:
        - npm ci
    build:
      commands:
         - npm run build
  artifacts:
    baseDirectory: build
    files:
      - '**/*'
  cache:
    paths:
      - node_modules/**/*

Service role

I had to create and add a service role.

First create a service role in IAM console, named it "AmplifyConsoleServiceRole-AmplifyRole":

Then add this role in the general settings of the Amplify application:

Amplify CLI to latest version

I had to set the Live package updates for the Amplify CLI to the latest version in build image settings:

To be continued...

Unfortunately this took too much time, so I have to do the last steps (4: API and database; 5: storage) another time. Maybe there will be some other challanges I can write down here.

Categories
Development

Node.js

Install Node.js on Windows

I am following this instructions: https://docs.microsoft.com/de-de/windows/nodejs/setup-on-windows

Download Node Version Manager (nvm) for Windows: https://github.com/coreybutler/nvm-windows#node-version-manager-nvm-for-windows
Current version: 1.1.7; nvm-setup.zip

Open cmd or GitBash and run 'nvm ls' to see installed Node versions.

Some npm commands:

# Show installed Nodes
nvm ls
# Show available versions
nvm list available
# Install latest version
nvm install latest
# Install latest LTS version (list available -> currently 12.19.0)
nvm install 12.19.0
# Use latest LTS version
nvm use 12.19.0
# Show npm version
npm --version

Categories
Java

Git

Short installation guide for Git with GitBash, SourceTree for a simple visual user interface and Git Staging View in Eclipse.

Git and GitBash

Download: https://gitforwindows.org
Install with defaults.

As there was no HOME environment variable set, my Git took some other HOME-like variable (like HOMEPATH, not sure), and this is a network share, so my Git performance was sometimes very poor.
To fix this, just set a HOME variable to your 'normal' profile folder:

Set a persistent environment variable from cmd.exe

setx HOME %%USERPROFILE%%

'set' sets your environment variable in your current shell only, persist it with 'setx'.

Another problem is, that the Git reporitory I tried to connect, has a SSL key signed by a non-public CA. This results to a "ssl pkix path validation failed" error.
To resolve this, I download the public certificate of the CA from webbrowser and add it to the truststore of Git.

Where is the cert store of git?

git config --system --list
http.sslcainfo={PathToGit}/mingw64/ssl/certs/ca-bundle.crt

To add the non-public CA cert to Git cert store just open ca-bundle.crt and the downloaded certificate with an text editor and copy the content of the certificate to the ca-bundle.

Additional I configured the sslBackend to the Windows Networking Layer:

# use SChannel, the built-in Windows networking layer. This means that it will use the Windows certificate storage mechanism and you do not need to explicitly configure the curl CA storage mechanism.
git config --global http.sslBackend schannel

Configure my user details:

git config --global user.name "Ingo Kaulbach"
git config --global user.email "ingo.kaulbach@covestro.com"

Some Git commands for Git configuration:

git config --local --list
git config --global --list
git config --system --list
 
git config --local --edit

Checkout / Clone of a project:

cd /path/to/my/workspace
git clone https://{Username}:{PersonalAccessToken}@gitlab.myserver.biz/project/project.git

SourceTree

Download: https://www.sourcetreeapp.com
Current Version: 3.3.9 (Windows)

Install with default settings, without Mercurial.

Open /path/to/my/workspace/project.

Eclipse

Open Windows -> Perspective -> Open Perspective -> Other -> Git.

In Git Repositories View: Add an existing local Git Repository to this view and open /path/to/my/workspace/project.

To Commit and Push changes only use the Git Staging view!!!

Categories
AWS Java

Credentials

What I want to achieve

In my past experiments the AWS credentials were 'magically' set in the background. To learn more about AWS credentials I will remove step by step the 'magic' and set credentials explicit in my code.

Cleanup

In my first experiment I set up the credentials on my Windows machine.
To ensure, that they are provided I test with my SNS-Test Program from my last post:

package aws;

import software.amazon.awssdk.services.sns.SnsClient;
import software.amazon.awssdk.services.sns.model.ListTopicsRequest;
import software.amazon.awssdk.services.sns.model.ListTopicsResponse;

public class CredentialsTest {

	public static void main(String[] args) {		
		SnsClient snsClient = SnsClient.builder().build();
		ListTopicsRequest request = ListTopicsRequest.builder().build();
		ListTopicsResponse result = snsClient.listTopics(request);
		System.out.println("Status was " + result.sdkHttpResponse().statusCode() + "\n\nTopics\n\n" + result.topics());
	}
}

Result: A list of my SNS topics

To remove the 'magic' I rename the files credentials and config in C:\Users\USERNAME\.aws folder to credentials_backup and config_backup.

Start CredentialsTest and the result: A list of my SNS topics.
So the credentials are provided by another mechanism.

Next try to remove the 'magic' I remove environment variables AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY and AWS_DEFAULT_REGION.
As I have started my IDE with this environment variables set, I need to restart IDE first.

Start CredentialsTest and the result:

Exception in thread "main" software.amazon.awssdk.core.exception.SdkClientException: Unable to load region from any of the providers in the chain software.amazon.awssdk.regions.providers.DefaultAwsRegionProviderChain@4372b9b6: [software.amazon.awssdk.regions.providers.SystemSettingsRegionProvider@3e6f3f28: Unable to load region from system settings. Region must be specified either via environment variable (AWS_REGION) or  system property (aws.region)., software.amazon.awssdk.regions.providers.AwsProfileRegionProvider@4816278d: No region provided in profile: default, software.amazon.awssdk.regions.providers.InstanceProfileRegionProvider@1ecee32c: Unable to contact EC2 metadata service.]
	at software.amazon.awssdk.core.exception.SdkClientException$BuilderImpl.build(SdkClientException.java:98)

Provide region:

SnsClient snsClient = SnsClient.builder().region(Region.EU_CENTRAL_1).build();

Result:

Exception in thread "main" software.amazon.awssdk.core.exception.SdkClientException: Unable to load credentials from any of the providers in the chain AwsCredentialsProviderChain(credentialsProviders=[SystemPropertyCredentialsProvider(), EnvironmentVariableCredentialsProvider(), WebIdentityTokenCredentialsProvider(), ProfileCredentialsProvider(), ContainerCredentialsProvider(), InstanceProfileCredentialsProvider()]) : [SystemPropertyCredentialsProvider(): Unable to load credentials from system settings. Access key must be specified either via environment variable (AWS_ACCESS_KEY_ID) or system property (aws.accessKeyId)., EnvironmentVariableCredentialsProvider(): Unable to load credentials from system settings. Access key must be specified either via environment variable (AWS_ACCESS_KEY_ID) or system property (aws.accessKeyId)., WebIdentityTokenCredentialsProvider(): Either the environment variable AWS_WEB_IDENTITY_TOKEN_FILE or the javaproperty aws.webIdentityTokenFile must be set., ProfileCredentialsProvider(): Profile file contained no credentials for profile 'default': ProfileFile(profiles=[]), ContainerCredentialsProvider(): Cannot fetch credentials from container - neither AWS_CONTAINER_CREDENTIALS_FULL_URI or AWS_CONTAINER_CREDENTIALS_RELATIVE_URI environment variables are set., InstanceProfileCredentialsProvider(): Unable to load credentials from service endpoint.]
	at software.amazon.awssdk.core.exception.SdkClientException$BuilderImpl.build(SdkClientException.java:98)

OK, looks good so far.
Remove region from code and restore files credentials and config in C:\Users\USERNAME\.aws folder.
Run CredentialsTest, Result: A list of my SNS topics.

Rename the files credentials and config in C:\Users\USERNAME\.aws folder to credentials_backup and config_backup again.
Run CredentialsTest, Result: Unable to load region error again.

The 'magic' has been removed,

ProfileCredentialsProvider

Restore files credentials and config in C:\Users\USERNAME\.aws folder.
Empty [default] block and create a new [CredentialsTest] block:

[default]

[CredentialsTest]
aws_access_key_id = My_AWS_Access_Key_Id
aws_secret_access_key = My_AWS_Secret_Access_Key
[default]

[CredentialsTest]
region = eu-central-1

Run CredentialsTest, Result:

2020-09-09 21:13:17 [main] WARN  software.amazon.awssdk.profiles.internal.ProfileFileReader:105 - Ignoring profile 'CredentialsTest' on line 3 because it did not start with 'profile ' and it was not 'default'.
Exception in thread "main" software.amazon.awssdk.core.exception.SdkClientException: Unable to load region from any of the providers in the chain software.amazon.awssdk.regions.providers.DefaultAwsRegionProviderChain@260e86a1: [software.amazon.awssdk.regions.providers.SystemSettingsRegionProvider@59e505b2: Unable to load region from system settings. Region must be specified either via environment variable (AWS_REGION) or  system property (aws.region)., software.amazon.awssdk.regions.providers.AwsProfileRegionProvider@8e50104: No region provided in profile: default, software.amazon.awssdk.regions.providers.InstanceProfileRegionProvider@43b6123e: Unable to contact EC2 metadata service.]

So I used to try to work with the ProfileCredentialsProvider this way:

import com.amazonaws.auth.profile.ProfileCredentialsProvider;
SnsClient snsClient = SnsClient.builder().credentialsProvider(new ProfileCredentialsProvider("CredentialsTest")).build();

Unfortunatly this won't compile because:

The method credentialsProvider(AwsCredentialsProvider) in the type AwsClientBuilder<SnsClientBuilder,
 SnsClient> is not applicable for the arguments (ProfileCredentialsProvider)

Refactor to:

import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
import software.amazon.awssdk.auth.credentials.ProfileCredentialsProvider;
AwsCredentialsProvider credentialsProvider = ProfileCredentialsProvider.builder().profileName("CredentialsTest").build();
SnsClient snsClient = SnsClient.builder().credentialsProvider(credentialsProvider).build();

Result:

2020-09-09 21:22:40 [main] WARN  software.amazon.awssdk.profiles.internal.ProfileFileReader:105 - Ignoring profile 'CredentialsTest' on line 3 because it did not start with 'profile ' and it was not 'default'.
Exception in thread "main" software.amazon.awssdk.core.exception.SdkClientException: Unable to load region from any of the providers in the chain software.amazon.awssdk.regions.providers.DefaultAwsRegionProviderChain@78f5c518: [software.amazon.awssdk.regions.providers.SystemSettingsRegionProvider@f107c50: Unable to load region from system settings. Region must be specified either via environment variable (AWS_REGION) or  system property (aws.region)., software.amazon.awssdk.regions.providers.AwsProfileRegionProvider@4ebff610: No region provided in profile: default, software.amazon.awssdk.regions.providers.InstanceProfileRegionProvider@8692d67: Unable to contact EC2 metadata service.]

Hmkay, enhance the code with a region; need to set this explicit, could not find any way to read this from the config file.

SnsClient snsClient = SnsClient.builder().credentialsProvider(credentialsProvider).region(Region.EU_CENTRAL_1).build();

Result: A list of my SNS topics.

Rename the files credentials and config in C:\Users\USERNAME\.aws folder to credentials_backup and config_backup again.
Run CredentialsTest, Result: Profile file contained no credentials for profile 'CredentialsTest' error.

Remove ProfileCredentialsProvider and Region from code.
Run CredentialsTest, Result: Unable to load region error again.

Own AwsCredentialsProvider implementation

Write an own credential provider, the simplest way:

package aws;

import software.amazon.awssdk.auth.credentials.AwsCredentials;
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;

public class IngosCredentialProvider implements AwsCredentialsProvider {

	public AwsCredentials resolveCredentials() {
		System.out.println("IngosCredentialProvider::resolveCredentials called");
		AwsCredentials credentials = new AwsCredentials() {
			
			public String secretAccessKey() {
				return "My_AWS_Secret_Access_Key";
			}
			
			public String accessKeyId() {
				return "My_AWS_Access_Key_Id";
			}
		};
		return credentials;
	}
}

Use your own credentials provider in code, don't forget the region:

package aws;

import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.sns.SnsClient;
import software.amazon.awssdk.services.sns.model.ListTopicsRequest;
import software.amazon.awssdk.services.sns.model.ListTopicsResponse;

public class CredentialsTest {

	public static void main(String[] args) {
		SnsClient snsClient = SnsClient.builder().credentialsProvider(new IngosCredentialProvider()).region(Region.EU_CENTRAL_1).build();
		ListTopicsRequest request = ListTopicsRequest.builder().build();
		ListTopicsResponse result = snsClient.listTopics(request);
		System.out.println("Status was " + result.sdkHttpResponse().statusCode() + "\n\nTopics\n\n" + result.topics());
	}
}

Run CredentialsTest, Result: A list of my SNS topics.