<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: NT</title>
    <description>The latest articles on DEV Community by NT (@nobudev7).</description>
    <link>https://dev.to/nobudev7</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1103392%2Fa0385173-4947-4c06-85a8-61f7a62bad91.png</url>
      <title>DEV Community: NT</title>
      <link>https://dev.to/nobudev7</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nobudev7"/>
    <language>en</language>
    <item>
      <title>Leaving DEV.to due to a bug</title>
      <dc:creator>NT</dc:creator>
      <pubDate>Sat, 20 Jan 2024 18:35:30 +0000</pubDate>
      <link>https://dev.to/nobudev7/leaving-devto-due-to-a-bug-2gj4</link>
      <guid>https://dev.to/nobudev7/leaving-devto-due-to-a-bug-2gj4</guid>
      <description>&lt;p&gt;I'm leaving DEV.to because of a bug that is intolerable. &lt;br&gt;
This bug was reported at least a year ago, but not addressed.&lt;br&gt;
There are 3 reports so far I could find.&lt;br&gt;
&lt;a href="https://github.com/forem/forem/issues/18939"&gt;https://github.com/forem/forem/issues/18939&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/forem/forem/issues/15208"&gt;https://github.com/forem/forem/issues/15208&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/forem/forem/issues/20469"&gt;https://github.com/forem/forem/issues/20469&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Wishing DEV.to and forem team to address it for others who are using this site.&lt;/p&gt;

&lt;p&gt;See me at my &lt;a href="https://nobudev7.github.io"&gt;GitHub Page&lt;/a&gt;&lt;/p&gt;

</description>
      <category>bug</category>
    </item>
    <item>
      <title>AWS Lightsail Java Server Setup Memo</title>
      <dc:creator>NT</dc:creator>
      <pubDate>Mon, 15 Jan 2024 16:54:35 +0000</pubDate>
      <link>https://dev.to/nobudev7/aws-lightsail-java-server-setup-memo-34c0</link>
      <guid>https://dev.to/nobudev7/aws-lightsail-java-server-setup-memo-34c0</guid>
      <description>&lt;p&gt;This is a journal of my setup for a Java server on AWS, Lightsail in particular for my &lt;a href="https://github.com/nobudev7/sumpdata"&gt;Sump Water Level Application&lt;/a&gt; project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Applications
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;OS: Amazon Linux 2023&lt;/li&gt;
&lt;li&gt;Reverse Proxy: Nginx&lt;/li&gt;
&lt;li&gt;Application: Spring Boot jar - as Java server&lt;/li&gt;
&lt;li&gt;DB: MySQL&lt;/li&gt;
&lt;li&gt;Cache: Redis&lt;/li&gt;
&lt;li&gt;Streamlit - Presentation of the Sump Water Level data&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why Lightsail
&lt;/h2&gt;

&lt;p&gt;Because this server is for a purpose of showcasing the Spring Boot application, the load is very light, almost nil. One VPS suffices hosting Nginx, MySQL and Redis. For a real-world application, with somewhat mild workload, I'd use separate AWS services, such as RDS for MySQL and ElastiCache for Redis.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup Steps
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Select Amazon Linux 2023 from the "Create Instance" page.
&lt;/h3&gt;

&lt;p&gt;I didn't choose Nginx instance because it's a Bitnami package with PHP, Memcache, and other frills I don't need. I chose 1GB RAM plan (the cheapest, $5/mo) to start with. Since it'll connect with just 1 Raspberry Pi and the Web UI will be basically only for 1 user (me), this should be sufficient.&lt;/p&gt;

&lt;p&gt;Note: Below in screenshot, I started with 512MB instance. But I moved it to 1GB instance.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9hcgg4o9t5w9n2d9hix6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9hcgg4o9t5w9n2d9hix6.png" alt="Lightsail Instance Selection" width="800" height="727"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyze928zp5e9ux8x9tatp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyze928zp5e9ux8x9tatp.png" alt="Instance Plan Selection" width="800" height="384"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In moving the instance from 512MB to 1G, I created a snapshot of 512MB instance, and created a new instance from it.&lt;br&gt;
Somehow, the new instance doesn't have a swap file (probably because the memory size is different), I created it manually.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; &lt;span class="nb"&gt;dd &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/zero &lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/swapfile &lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1M &lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1024
 &lt;span class="nb"&gt;chmod &lt;/span&gt;600 /swapfile
 mkswap /swapfile
 swapon /swapfile
 swapon &lt;span class="nt"&gt;-s&lt;/span&gt;
 free
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, edited &lt;code&gt;/etc/fstab&lt;/code&gt; and added the line on the bottom so that swap file will be used after reboot.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/swapfile swap swap defaults 0 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Set up the basics
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Attach Static IP&lt;/li&gt;
&lt;li&gt;Add SSH public key from my laptop&lt;/li&gt;
&lt;li&gt;Give a host name to the IP locally (/etc/hosts) - I named it &lt;code&gt;sumpserver&lt;/code&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;ssh ec2-user@sumpserver   
The authenticity of host &lt;span class="s1"&gt;'sumpserver (xx.xx.xx.xx)'&lt;/span&gt; can&lt;span class="s1"&gt;'t be established.
ED25519 key fingerprint is SHA256:abdcABCD1234...
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '&lt;/span&gt;sumpserver&lt;span class="s1"&gt;' (ED25519) to the list of known hosts.
   ,     #_
   ~\_  ####_        Amazon Linux 2023
  ~~  \_#####\
  ~~     \###|
  ~~       \#/ ___   https://aws.amazon.com/linux/amazon-linux-2023
   ~~       V~'&lt;/span&gt; &lt;span class="s1"&gt;'-&amp;gt;
    ~~~         /
      ~~._.   _/
         _/ _/
       _/m/'&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;ec2-user@ip-xx-xx-xx-xxx ~]&lt;span class="err"&gt;$&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Set the time zone. Add below in &lt;code&gt;.bash_profile&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;TZ&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'America/New_York'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;export &lt;/span&gt;TZ
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Edit prompt
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PS1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'\[\e[1;106m\]\u \W\[\e[m\] $ '&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Install MySQL
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;su
dnf upgrade
dnf localinstall &lt;span class="nt"&gt;-y&lt;/span&gt; https://dev.mysql.com/get/mysql80-community-release-el9-1.noarch.rpm
dnf &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; mysql mysql-server mysql-community-client
systemctl start mysqld.service
systemctl status mysqld.service
systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;mysqld.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check root temporary password&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s1"&gt;'temporary password'&lt;/span&gt; /var/log/mysqld.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Login to MySQL&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mysql &lt;span class="nt"&gt;-u&lt;/span&gt; root &lt;span class="nt"&gt;-p&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create user and database&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;USER&lt;/span&gt; &lt;span class="s1"&gt;'root'&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="s1"&gt;'localhost'&lt;/span&gt; &lt;span class="n"&gt;IDENTIFIED&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;myrootpassword_here&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;USER&lt;/span&gt; &lt;span class="s1"&gt;'sumpadmin'&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="s1"&gt;'%'&lt;/span&gt; &lt;span class="n"&gt;identified&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;sumpserver_user_password&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;DATABASE&lt;/span&gt; &lt;span class="n"&gt;sumpdata&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;ALL&lt;/span&gt; &lt;span class="k"&gt;PRIVILEGES&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;sumpdata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="s1"&gt;'sumpadmin'&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="s1"&gt;'%'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Install Nginx
&lt;/h3&gt;

&lt;p&gt;Installation of Nginx itself is straightforward.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;su
dnf &lt;span class="nt"&gt;-y&lt;/span&gt; update
dnf list &lt;span class="nt"&gt;--available&lt;/span&gt; nginx
dnf &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="nb"&gt;install &lt;/span&gt;nginx
nginx &lt;span class="nt"&gt;-v&lt;/span&gt;
systemctl start nginx.service
systemctl status nginx.service
systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;nginx.service
curl http://localhost
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Point your browser to the public IP to check it's accessible from the internet.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmr9bbnkjnjklxglik343.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmr9bbnkjnjklxglik343.png" alt="Nginx on Browser" width="800" height="529"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Let's Encrypt Certificate
&lt;/h4&gt;

&lt;p&gt;At this point, it only supports non-secure HTTP only. For HTTPS, I did the following.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Obtain a domain and associate with AWS IP.&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F28dxh2nlrw7rmh0jurxs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F28dxh2nlrw7rmh0jurxs.png" alt="Domain to IP" width="800" height="292"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Install Certbot&lt;/strong&gt;&lt;br&gt;
Certbot is a CLI that helps to obtain and maintain &lt;a href="https://letsencrypt.org"&gt;Let's Encrypt&lt;/a&gt; cert.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;su
dnf &lt;span class="nb"&gt;install &lt;/span&gt;certbot python3-certbot-nginx &lt;span class="nt"&gt;-y&lt;/span&gt;
certbot &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before asking the &lt;code&gt;certbot&lt;/code&gt; to obtain a SSL cert, configure Nginx server setting for the domain, so that &lt;code&gt;certbot&lt;/code&gt; can edit the config for the matching domain.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;/etc/nginx/conf.d/sumpdata.conf&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;server&lt;/span&gt; {
    &lt;span class="n"&gt;listen&lt;/span&gt;       &lt;span class="m"&gt;80&lt;/span&gt;;
    &lt;span class="n"&gt;listen&lt;/span&gt;  [::]:&lt;span class="m"&gt;80&lt;/span&gt;;
    &lt;span class="n"&gt;server_name&lt;/span&gt;  &lt;span class="n"&gt;sumpdata&lt;/span&gt;.&lt;span class="n"&gt;nobudev7&lt;/span&gt;.&lt;span class="n"&gt;com&lt;/span&gt;;


    &lt;span class="n"&gt;location&lt;/span&gt; / {
        &lt;span class="n"&gt;root&lt;/span&gt;   /&lt;span class="n"&gt;usr&lt;/span&gt;/&lt;span class="n"&gt;share&lt;/span&gt;/&lt;span class="n"&gt;nginx&lt;/span&gt;/&lt;span class="n"&gt;html&lt;/span&gt;;
        &lt;span class="n"&gt;index&lt;/span&gt;  &lt;span class="n"&gt;index&lt;/span&gt;.&lt;span class="n"&gt;html&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;.&lt;span class="n"&gt;htm&lt;/span&gt;;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Test the conf file&lt;/span&gt;
nginx &lt;span class="nt"&gt;-t&lt;/span&gt;
&lt;span class="c"&gt;# Reload the conf&lt;/span&gt;
nginx &lt;span class="nt"&gt;-s&lt;/span&gt; reload
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Run Certbot&lt;/strong&gt;&lt;br&gt;
Now, run &lt;code&gt;certbot&lt;/code&gt; to obtain an SSL cert for the domain.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;certbot &lt;span class="nt"&gt;--nginx&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; sumpdata.nobudev7.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After successfully obtained and installed the cert to Nginx configuration, the above created &lt;code&gt;sumpdata.conf&lt;/code&gt; was edited by Certbot as follows.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;server&lt;/span&gt; {
    &lt;span class="n"&gt;server_name&lt;/span&gt;  &lt;span class="n"&gt;sumpdata&lt;/span&gt;.&lt;span class="n"&gt;nobudev7&lt;/span&gt;.&lt;span class="n"&gt;com&lt;/span&gt;;


    &lt;span class="n"&gt;location&lt;/span&gt; / {
        &lt;span class="n"&gt;root&lt;/span&gt;   /&lt;span class="n"&gt;usr&lt;/span&gt;/&lt;span class="n"&gt;share&lt;/span&gt;/&lt;span class="n"&gt;nginx&lt;/span&gt;/&lt;span class="n"&gt;html&lt;/span&gt;;
        &lt;span class="n"&gt;index&lt;/span&gt;  &lt;span class="n"&gt;index&lt;/span&gt;.&lt;span class="n"&gt;html&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;.&lt;span class="n"&gt;htm&lt;/span&gt;;
    }

    &lt;span class="n"&gt;listen&lt;/span&gt; [::]:&lt;span class="m"&gt;443&lt;/span&gt; &lt;span class="n"&gt;ssl&lt;/span&gt; &lt;span class="n"&gt;ipv6only&lt;/span&gt;=&lt;span class="n"&gt;on&lt;/span&gt;; &lt;span class="c"&gt;# managed by Certbot
&lt;/span&gt;    &lt;span class="n"&gt;listen&lt;/span&gt; &lt;span class="m"&gt;443&lt;/span&gt; &lt;span class="n"&gt;ssl&lt;/span&gt;; &lt;span class="c"&gt;# managed by Certbot
&lt;/span&gt;    &lt;span class="n"&gt;ssl_certificate&lt;/span&gt; /&lt;span class="n"&gt;etc&lt;/span&gt;/&lt;span class="n"&gt;letsencrypt&lt;/span&gt;/&lt;span class="n"&gt;live&lt;/span&gt;/&lt;span class="n"&gt;sumpdata&lt;/span&gt;.&lt;span class="n"&gt;nobudev7&lt;/span&gt;.&lt;span class="n"&gt;com&lt;/span&gt;/&lt;span class="n"&gt;fullchain&lt;/span&gt;.&lt;span class="n"&gt;pem&lt;/span&gt;; &lt;span class="c"&gt;# managed by Certbot
&lt;/span&gt;    &lt;span class="n"&gt;ssl_certificate_key&lt;/span&gt; /&lt;span class="n"&gt;etc&lt;/span&gt;/&lt;span class="n"&gt;letsencrypt&lt;/span&gt;/&lt;span class="n"&gt;live&lt;/span&gt;/&lt;span class="n"&gt;sumpdata&lt;/span&gt;.&lt;span class="n"&gt;nobudev7&lt;/span&gt;.&lt;span class="n"&gt;com&lt;/span&gt;/&lt;span class="n"&gt;privkey&lt;/span&gt;.&lt;span class="n"&gt;pem&lt;/span&gt;; &lt;span class="c"&gt;# managed by Certbot
&lt;/span&gt;    &lt;span class="n"&gt;include&lt;/span&gt; /&lt;span class="n"&gt;etc&lt;/span&gt;/&lt;span class="n"&gt;letsencrypt&lt;/span&gt;/&lt;span class="n"&gt;options&lt;/span&gt;-&lt;span class="n"&gt;ssl&lt;/span&gt;-&lt;span class="n"&gt;nginx&lt;/span&gt;.&lt;span class="n"&gt;conf&lt;/span&gt;; &lt;span class="c"&gt;# managed by Certbot
&lt;/span&gt;    &lt;span class="n"&gt;ssl_dhparam&lt;/span&gt; /&lt;span class="n"&gt;etc&lt;/span&gt;/&lt;span class="n"&gt;letsencrypt&lt;/span&gt;/&lt;span class="n"&gt;ssl&lt;/span&gt;-&lt;span class="n"&gt;dhparams&lt;/span&gt;.&lt;span class="n"&gt;pem&lt;/span&gt;; &lt;span class="c"&gt;# managed by Certbot
&lt;/span&gt;
}
&lt;span class="n"&gt;server&lt;/span&gt; {
    &lt;span class="n"&gt;if&lt;/span&gt; ($&lt;span class="n"&gt;host&lt;/span&gt; = &lt;span class="n"&gt;sumpdata&lt;/span&gt;.&lt;span class="n"&gt;nobudev7&lt;/span&gt;.&lt;span class="n"&gt;com&lt;/span&gt;) {
        &lt;span class="n"&gt;return&lt;/span&gt; &lt;span class="m"&gt;301&lt;/span&gt; &lt;span class="n"&gt;https&lt;/span&gt;://$&lt;span class="n"&gt;host&lt;/span&gt;$&lt;span class="n"&gt;request_uri&lt;/span&gt;;
    } &lt;span class="c"&gt;# managed by Certbot
&lt;/span&gt;

    &lt;span class="n"&gt;listen&lt;/span&gt;       &lt;span class="m"&gt;80&lt;/span&gt;;
    &lt;span class="n"&gt;listen&lt;/span&gt;  [::]:&lt;span class="m"&gt;80&lt;/span&gt;;
    &lt;span class="n"&gt;server_name&lt;/span&gt;  &lt;span class="n"&gt;sumpdata&lt;/span&gt;.&lt;span class="n"&gt;nobudev7&lt;/span&gt;.&lt;span class="n"&gt;com&lt;/span&gt;;
    &lt;span class="n"&gt;return&lt;/span&gt; &lt;span class="m"&gt;404&lt;/span&gt;; &lt;span class="c"&gt;# managed by Certbot
&lt;/span&gt;

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that Certbot made non-secure HTTP (port 80) to automatically redirect to HTTPS.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Configure Auto Renew&lt;/strong&gt;&lt;br&gt;
During the above process, &lt;code&gt;certbot&lt;/code&gt; output the following.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Certbot has set up a scheduled task to automatically renew this certificate in the background.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, checking timers revealed that nothing is set up for certbot.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;systemctl list-timers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After reading various posts, I figured that certbot timer name is &lt;code&gt;certbot-renew.timer&lt;/code&gt; not &lt;code&gt;certbot.timer&lt;/code&gt; as widely referenced. Even Certbot itself might have a bug in setting the timer. Below, I set up the timer manually.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Where is the certbot timer and service?&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt; /lib/systemd/system/certbot&lt;span class="k"&gt;*&lt;/span&gt;
/lib/systemd/system/certbot-renew.service
/lib/systemd/system/certbot-renew.timer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Try to see if the service works, and check the status.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;systemctl start certbot-renew.service
systemctl status certbot-renew.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;○ certbot-renew.service - This service automatically renews any certbot certificates found
     Loaded: loaded (/usr/lib/systemd/system/certbot-renew.service; static)
     Active: inactive (dead) since Mon 2024-01-15 12:57:33 EST; 31s ago
TriggeredBy: ○ certbot-renew.timer
    Process: 69039 ExecStart=/usr/bin/certbot renew --noninteractive --no-random-sleep-on-renew $PRE_HOOK $POST_HOOK $RENEW_HOOK $DEPLOY_HOOK $CERTBOT_ARGS (code=exited, status=0/SUCCESS)
   Main PID: 69039 (code=exited, status=0/SUCCESS)
        CPU: 500ms

Jan 15 12:57:33 ip-xx-xx-xx-xx.ec2.internal certbot[69039]: Processing /etc/letsencrypt/renewal/sumpdata.nobudev7.com.conf
Jan 15 12:57:33 ip-xx-xx-xx-xx.ec2.internal certbot[69039]: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Jan 15 12:57:33 ip-xx-xx-xx-xx.ec2.internal certbot[69039]: Certificate not yet due for renewal
Jan 15 12:57:33 ip-xx-xx-xx-xx.ec2.internal certbot[69039]: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Jan 15 12:57:33 ip-xx-xx-xx-xx.ec2.internal certbot[69039]: The following certificates are not due for renewal yet:
Jan 15 12:57:33 ip-xx-xx-xx-xx.ec2.internal certbot[69039]:   /etc/letsencrypt/live/sumpdata.nobudev7.com/fullchain.pem expires on 2024-04-14 (skipped)
Jan 15 12:57:33 ip-xx-xx-xx-xx.ec2.internal certbot[69039]: No renewals were attempted.
Jan 15 12:57:33 ip-xx-xx-xx-xx.ec2.internal certbot[69039]: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Jan 15 12:57:33 ip-xx-xx-xx-xx.ec2.internal systemd[1]: certbot-renew.service: Deactivated successfully.
Jan 15 12:57:33 ip-xx-xx-xx-xx.ec2.internal systemd[1]: Finished certbot-renew.service - This service automatically renews any certbot certificates found.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start the timer&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;systemctl start certbot-renew.timer
&lt;span class="c"&gt;# Check if it's scheduled.&lt;/span&gt;
systemctl list-timers certbot-renew.timer
&lt;span class="c"&gt;# OUTPUT&lt;/span&gt;
NEXT                        LEFT     LAST PASSED UNIT                ACTIVATES
Tue 2024-01-16 06:58:43 EST 16h left -    -      certbot-renew.timer certbot-renew.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: Amazon Linux 2023 doesn't come with &lt;code&gt;crontab&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Install Git
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;su
dnf &lt;span class="nb"&gt;install &lt;/span&gt;git &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;span class="c"&gt;# exit to ec2-user&lt;/span&gt;
&lt;span class="nb"&gt;exit
&lt;/span&gt;git &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  6. Install Redis
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dnf list &lt;span class="nt"&gt;--available&lt;/span&gt; |grep redis
&lt;span class="c"&gt;# OUTPUT&lt;/span&gt;
redis6.x86_64         6.2.13-1.amzn2023.0.1    amazonlinux
redis6-devel.x86_64   6.2.13-1.amzn2023.0.1    amazonlinux
redis6-doc.noarch     6.2.13-1.amzn2023.0.1    amazonlinux

dnf &lt;span class="nb"&gt;install &lt;/span&gt;redis6 &lt;span class="nt"&gt;-y&lt;/span&gt;
systemctl start redis6
systemctl status redis6
systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;redis6
redis6-cli 
&lt;span class="c"&gt;# OUTPUT&lt;/span&gt;
127.0.0.1:6379&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  7. Install Java (Runtime)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check Java availability&lt;/span&gt;
dnf list &lt;span class="nt"&gt;--available&lt;/span&gt; java-21-amazon-corretto
&lt;span class="c"&gt;# Install&lt;/span&gt;
dnf &lt;span class="nb"&gt;install &lt;/span&gt;java-21-amazon-corretto &lt;span class="nt"&gt;-y&lt;/span&gt;
java &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  8. Deploy Jar file from Git
&lt;/h3&gt;

&lt;p&gt;The Git Action (discussed in &lt;a href="https://dev.to/nobudev7/github-actions-for-jar-file-deployment-204"&gt;GitHub Actions for Jar file deployment&lt;/a&gt;) build a jar file from the Java project.&lt;br&gt;
While the intention is to download it manually, I put all commands in a shell script.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="c"&gt;# Remove files named sumpdata*.jar&lt;/span&gt;
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; sumpdata&lt;span class="k"&gt;*&lt;/span&gt;.jar

&lt;span class="nv"&gt;latestjar&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-s&lt;/span&gt; https://api.github.com/repos/nobudev7/sumpdata/actions/artifacts &lt;span class="se"&gt;\&lt;/span&gt;
    | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.artifacts | max_by(.created_at) | .archive_download_url'&lt;/span&gt;
&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Downloading &lt;/span&gt;&lt;span class="nv"&gt;$latestjar&lt;/span&gt;&lt;span class="s2"&gt;..."&lt;/span&gt;

curl &lt;span class="nt"&gt;-L&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; artifact.zip&lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Accept: application/vnd.github+json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &amp;lt;github_token&amp;gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-GitHub-Api-Version: 2022-11-28"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$latestjar&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt; &lt;span class="nt"&gt;-eq&lt;/span&gt; 0 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;unzip artifact.zip
  &lt;span class="nb"&gt;rm &lt;/span&gt;artifact.zip
&lt;span class="k"&gt;else
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Downloading Jar failed: &lt;/span&gt;&lt;span class="nv"&gt;$latestjar&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  9. Run Jar application
&lt;/h3&gt;

&lt;p&gt;To test if the Jar application properly work, start it with proper variables. In my case, I have different MySQL user/pass than what's in the &lt;code&gt;application.properties&lt;/code&gt; file, I pass it via environment variables (can be &lt;code&gt;Dxxx=yyy&lt;/code&gt; param as well).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;SPRING_DATASOURCE_USERNAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;mysql_user&amp;gt; &lt;span class="nv"&gt;SPRING_DATASOURCE_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;s&amp;lt;mysql_pass&amp;gt; java &lt;span class="nt"&gt;-jar&lt;/span&gt; sumpdata-0.2.0.jar 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add an entry from the local instance for testing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s1"&gt;'http://localhost:8080/devices/2/entries?measuredOn=2023-10-24T16:37:06&amp;amp;value=12.2'&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: text/plain'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check the MySQL table.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;mysql&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;data_entry&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;----------+----------------------------+-------+&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;deviceid&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;measured_on&lt;/span&gt;                &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;----------+----------------------------+-------+&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt;        &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;2023&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;37&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;06&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;000000&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;   &lt;span class="mi"&gt;122&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;----------+----------------------------+-------+&lt;/span&gt;
&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;row&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt; &lt;span class="n"&gt;sec&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  9. Run a Jar app as a service
&lt;/h3&gt;

&lt;p&gt;It seems that the &lt;em&gt;current&lt;/em&gt; way of setting up a service is to use &lt;code&gt;systemctl edit &amp;lt;service_name&amp;gt;&lt;/code&gt;.&lt;br&gt;
However, if you run it the first time, on Amazon Linux 2023, it shows an error and suggests &lt;code&gt;--force&lt;/code&gt; option.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# systemctl edit sumpdata&lt;/span&gt;
No files found &lt;span class="k"&gt;for &lt;/span&gt;sumpdata.service.
Run &lt;span class="s1"&gt;'systemctl edit --force --full sumpdata.service'&lt;/span&gt; to create a new unit.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running it as suggested, an editor (nano) shows up. This will become the service file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="n"&gt;Unit&lt;/span&gt;]
&lt;span class="n"&gt;Description&lt;/span&gt;=&lt;span class="n"&gt;Samp&lt;/span&gt; &lt;span class="n"&gt;Data&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;
&lt;span class="n"&gt;After&lt;/span&gt;=&lt;span class="n"&gt;syslog&lt;/span&gt;.&lt;span class="n"&gt;target&lt;/span&gt; &lt;span class="n"&gt;network&lt;/span&gt;.&lt;span class="n"&gt;target&lt;/span&gt;

[&lt;span class="n"&gt;Service&lt;/span&gt;]
&lt;span class="n"&gt;SuccessExitStatus&lt;/span&gt;=&lt;span class="m"&gt;143&lt;/span&gt;

&lt;span class="n"&gt;User&lt;/span&gt;=&lt;span class="n"&gt;ec2&lt;/span&gt;-&lt;span class="n"&gt;user&lt;/span&gt;
&lt;span class="n"&gt;Group&lt;/span&gt;=&lt;span class="n"&gt;ec2&lt;/span&gt;-&lt;span class="n"&gt;user&lt;/span&gt;

&lt;span class="n"&gt;Type&lt;/span&gt;=&lt;span class="n"&gt;simple&lt;/span&gt;

&lt;span class="n"&gt;WorkingDirectory&lt;/span&gt;=/&lt;span class="n"&gt;home&lt;/span&gt;/&lt;span class="n"&gt;ec2&lt;/span&gt;-&lt;span class="n"&gt;user&lt;/span&gt;/&lt;span class="n"&gt;sumpdata&lt;/span&gt;
&lt;span class="n"&gt;ExecStart&lt;/span&gt;=/&lt;span class="n"&gt;usr&lt;/span&gt;/&lt;span class="n"&gt;bin&lt;/span&gt;/&lt;span class="n"&gt;java&lt;/span&gt; -&lt;span class="n"&gt;jar&lt;/span&gt; &lt;span class="n"&gt;sumpdata&lt;/span&gt;.&lt;span class="n"&gt;jar&lt;/span&gt;
&lt;span class="n"&gt;ExecStop&lt;/span&gt;=/&lt;span class="n"&gt;bin&lt;/span&gt;/&lt;span class="n"&gt;kill&lt;/span&gt; -&lt;span class="m"&gt;15&lt;/span&gt; $&lt;span class="n"&gt;MAINPID&lt;/span&gt;

[&lt;span class="n"&gt;Install&lt;/span&gt;]
&lt;span class="n"&gt;WantedBy&lt;/span&gt;=&lt;span class="n"&gt;multi&lt;/span&gt;-&lt;span class="n"&gt;user&lt;/span&gt;.&lt;span class="n"&gt;target&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save the file, but don't change the file name, as the file name is temporary and the &lt;code&gt;systemctl&lt;/code&gt; will properly move it to &lt;code&gt;/etc/systemd/system&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Note that, the above service file doesn't have any &lt;code&gt;Environment&lt;/code&gt; values. That is to be written under &lt;code&gt;override.conf&lt;/code&gt;, by &lt;code&gt;systemctl edit &amp;lt;service_name&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The same (nano) editor opens up, and between comment lines, add the env variables.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="c"&gt;### Editing /etc/systemd/system/sumpdata.service.d/override.conf
### Anything between here and the comment below will become the new contents of the file
&lt;/span&gt;
[&lt;span class="n"&gt;Service&lt;/span&gt;]
&lt;span class="n"&gt;Environment&lt;/span&gt;=&lt;span class="s2"&gt;"SPRING_DATASOURCE_USERNAME=dbuser"&lt;/span&gt;
&lt;span class="n"&gt;Environment&lt;/span&gt;=&lt;span class="s2"&gt;"SPRING_DATASOURCE_PASSWORD=dbpassword"&lt;/span&gt;

&lt;span class="c"&gt;### Lines below this comment will be discarded
&lt;/span&gt;
&lt;span class="c"&gt;### /etc/systemd/system/sumpdata.service
# [Unit]
# Description=Samp Data service
# After=syslog.target network.target
&lt;/span&gt;...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After saving the override, a conf file is created.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;ll /etc/systemd/system/sumpdata.service.d
total 4
&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; 1 root root 125 Jan 20 10:36 override.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Register and start the service, and check status.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;systemctl daemon-reload
&lt;span class="nv"&gt;$ &lt;/span&gt;systemctl start sumpdata.service
&lt;span class="nv"&gt;$ &lt;/span&gt;systemctl status sumpdata.service
● sumpdata.service - Samp Data service
     Loaded: loaded &lt;span class="o"&gt;(&lt;/span&gt;/etc/systemd/system/sumpdata.service&lt;span class="p"&gt;;&lt;/span&gt; disabled&lt;span class="p"&gt;;&lt;/span&gt; preset: disabled&lt;span class="o"&gt;)&lt;/span&gt;
    Drop-In: /etc/systemd/system/sumpdata.service.d
             └─override.conf
     Active: active &lt;span class="o"&gt;(&lt;/span&gt;running&lt;span class="o"&gt;)&lt;/span&gt; since Sat 2024-01-20 10:21:51 EST&lt;span class="p"&gt;;&lt;/span&gt; 1s ago
   Main PID: 129986 &lt;span class="o"&gt;(&lt;/span&gt;java&lt;span class="o"&gt;)&lt;/span&gt;
      Tasks: 13 &lt;span class="o"&gt;(&lt;/span&gt;limit: 1061&lt;span class="o"&gt;)&lt;/span&gt;
     Memory: 69.9M
        CPU: 2.674s
     CGroup: /system.slice/sumpdata.service
             └─129986 /usr/bin/java &lt;span class="nt"&gt;-jar&lt;/span&gt; sumpdata.jar
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make the service start automatically.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;sumpdata.service
Created symlink /etc/systemd/system/multi-user.target.wants/sumpdata.service → /etc/systemd/system/sumpdata.service.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  X. Install Streamlit and clone SumpChart
&lt;/h3&gt;

&lt;p&gt;Clone SumpChart&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/ntamagawa/sumpchart.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
    </item>
    <item>
      <title>GitHub Actions for Jar file deployment</title>
      <dc:creator>NT</dc:creator>
      <pubDate>Sat, 13 Jan 2024 23:16:01 +0000</pubDate>
      <link>https://dev.to/nobudev7/github-actions-for-jar-file-deployment-204</link>
      <guid>https://dev.to/nobudev7/github-actions-for-jar-file-deployment-204</guid>
      <description>&lt;p&gt;This is a memo for a &lt;a href="https://github.com/ntamagawa/sumpdata"&gt;GitHub project&lt;/a&gt; that I'm planning to do CI/CD into an AWS instance (likely Lightsail AL2023).&lt;/p&gt;

&lt;p&gt;Reference: &lt;a href="https://medium.com/@alexander.volminger/ci-cd-for-java-maven-using-github-actions-d009a7cb4b8f"&gt;CI/CD for Java Maven using GitHub Actions&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  CI/CD Flow
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;On PR merge to the &lt;code&gt;main&lt;/code&gt; branch, run a GitHub action to create a Jar file (aka artifact). &lt;/li&gt;
&lt;li&gt;Upload the artifact - meaning make the artifact public&lt;/li&gt;
&lt;li&gt;Download the artifact from GitHub&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This flow can be done more sophisticated way using a CI/CD tool, but for my purpose, automatically packaging a jar then download using curl is good enough.&lt;/p&gt;

&lt;h2&gt;
  
  
  GitHub action
&lt;/h2&gt;

&lt;p&gt;Below action set up JDK 21, build the package using Maven, then "upload" the jar file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Package Jar with Maven&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;main"&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;workflow_dispatch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v3&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up JDK &lt;/span&gt;&lt;span class="m"&gt;21&lt;/span&gt;
      &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-java@v3&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;java-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;21'&lt;/span&gt;
        &lt;span class="na"&gt;distribution&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;temurin'&lt;/span&gt;
        &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;maven&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build with Maven&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mvn -B package --file pom.xml -DskipTests&lt;/span&gt;

    &lt;span class="c1"&gt;# https://medium.com/@alexander.volminger/ci-cd-for-java-maven-using-github-actions-d009a7cb4b8f&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Upload jar&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mkdir staging &amp;amp;&amp;amp; cp target/sumpdata-*.jar staging&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/upload-artifact@v4&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Package&lt;/span&gt;
        &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;staging&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, the concept of "upload" is a little confusing. What the GitHub action &lt;a href="https://github.com/actions/upload-artifact"&gt;upload-artifact&lt;/a&gt; does is to "copy" the jar file to a publicly accessible folder.&lt;/p&gt;

&lt;h2&gt;
  
  
  Curl Command to Download Jar
&lt;/h2&gt;

&lt;p&gt;Use GitHub REST API, in particular, &lt;a href="https://docs.github.com/en/rest/actions/artifacts?apiVersion=2022-11-28"&gt;GitHub Actions Artifact&lt;/a&gt; endpoint.&lt;/p&gt;

&lt;h3&gt;
  
  
  List artifacts
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://api.github.com/repos/&amp;lt;Owner&amp;gt;/&amp;lt;Repo&amp;gt;/actions/artifacts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"total_count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"artifacts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1167583720&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"node_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"MDg6QXJ0aWZhY3QxMTY3NTgzNzIw"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Package"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"size_in_bytes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;58991355&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://api.github.com/repos/ntamagawa/sumpdata/actions/artifacts/1167583720"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"archive_download_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://api.github.com/repos/ntamagawa/sumpdata/actions/artifacts/1167583720/zip"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"expired"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"created_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2024-01-13T22:38:33Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"updated_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2024-01-13T22:38:33Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"expires_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2024-04-12T22:38:10Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"workflow_run"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;7515265725&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"repository_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;705318802&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"head_repository_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;705318802&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"head_branch"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"head_sha"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fe3baccee31adbc74b1999c8c3e5c46709bd5ca7"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1167559585&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"node_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"MDg6QXJ0aWZhY3QxMTY3NTU5NTg1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Package"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"size_in_bytes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;58991346&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://api.github.com/repos/ntamagawa/sumpdata/actions/artifacts/1167559585"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"archive_download_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://api.github.com/repos/ntamagawa/sumpdata/actions/artifacts/1167559585/zip"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"expired"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"created_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2024-01-13T21:52:17Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"updated_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2024-01-13T21:52:17Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"expires_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2024-04-12T21:51:57Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"workflow_run"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;7515043712&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"repository_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;705318802&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"head_repository_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;705318802&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"head_branch"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"head_sha"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fe3baccee31adbc74b1999c8c3e5c46709bd5ca7"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: It seems that there's no access privilege is required for a public repo.&lt;/p&gt;

&lt;h3&gt;
  
  
  Download the artifact
&lt;/h3&gt;

&lt;p&gt;I take the &lt;code&gt;archive_download_url&lt;/code&gt; from the latest artifact, then download as a zip file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-L&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; artifact.zip &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Accept: application/vnd.github+json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &amp;lt;github_token&amp;gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-GitHub-Api-Version: 2022-11-28"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
https://api.github.com/repos/ntamagawa/sumpdata/actions/artifacts/1167559585/zip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that you need to generate your own GitHub personal access tokens. See &lt;a href="https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens"&gt;Managing your personal access tokens&lt;/a&gt;.&lt;br&gt;
For the above REST API &lt;code&gt;repos/.../actions/artifacts/...&lt;/code&gt;, a &lt;code&gt;Read&lt;/code&gt; access is required. &lt;a href="https://docs.github.com/en/rest/authentication/permissions-required-for-fine-grained-personal-access-tokens?apiVersion=2022-11-28#deployments"&gt;This page &lt;/a&gt;lists what permission is required for REST APIs.&lt;/p&gt;

&lt;p&gt;Then, unzip the file which contains the generated jar file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;unzip  artifact.zip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
    </item>
    <item>
      <title>Docker Nginx Memo</title>
      <dc:creator>NT</dc:creator>
      <pubDate>Sat, 06 Jan 2024 19:04:04 +0000</pubDate>
      <link>https://dev.to/nobudev7/docker-nginx-memo-4mmk</link>
      <guid>https://dev.to/nobudev7/docker-nginx-memo-4mmk</guid>
      <description>&lt;h2&gt;
  
  
  Start Nginx on Docker
&lt;/h2&gt;

&lt;p&gt;To run Nginx on Docker:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run --name nginx -p 80:80 -d nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To restart&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker start -a nginx &amp;amp;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check if the docker instance working&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://localhost/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fcbiIB1b--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/byx5be97zybcq3fp0p7t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fcbiIB1b--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/byx5be97zybcq3fp0p7t.png" alt="Nginx on Browser" width="800" height="476"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Hello World
&lt;/h2&gt;

&lt;p&gt;Login to shell&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker exec -it nginx /bin/bash 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nginx docker image doesn't include &lt;code&gt;vi&lt;/code&gt; so I needed to install it (technically, below install &lt;code&gt;vim&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apt update &amp;amp;&amp;amp; apt install -y vim
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create index.html&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;cd /usr/share/nginx/html
vi index2.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The content of &lt;code&gt;index2.html&lt;/code&gt; file might be something like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;
&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Hello World&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Hello World!&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the browser, access to &lt;code&gt;http://localhost/index2.html&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vMPSeaiy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7chtzfsm3mymvlmg7nw5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vMPSeaiy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7chtzfsm3mymvlmg7nw5.png" alt="Hello World HTML file" width="800" height="476"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Reverse Proxy
&lt;/h2&gt;

&lt;p&gt;To use Nginx for the development environment for the &lt;a href="https://github.com/ntamagawa/sumpdata"&gt;Sump Data Project&lt;/a&gt;, Nginx is configured as a reverse proxy server.&lt;/p&gt;

&lt;p&gt;/etc/nginx/conf.d/default.conf&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="err"&gt;server&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
    &lt;span class="err"&gt;listen&lt;/span&gt;       &lt;span class="err"&gt;80&lt;/span&gt;&lt;span class="c"&gt;;
&lt;/span&gt;    &lt;span class="err"&gt;listen&lt;/span&gt;  &lt;span class="nn"&gt;[::]&lt;/span&gt;&lt;span class="err"&gt;:80&lt;/span&gt;&lt;span class="c"&gt;;
&lt;/span&gt;    &lt;span class="err"&gt;server_name&lt;/span&gt;  &lt;span class="err"&gt;localhost&lt;/span&gt;&lt;span class="c"&gt;;
&lt;/span&gt;
    &lt;span class="c"&gt;#access_log  /var/log/nginx/host.access.log  main;
&lt;/span&gt;
    &lt;span class="err"&gt;location&lt;/span&gt; &lt;span class="err"&gt;/&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
        &lt;span class="err"&gt;root&lt;/span&gt;   &lt;span class="err"&gt;/usr/share/nginx/html&lt;/span&gt;&lt;span class="c"&gt;;
&lt;/span&gt;        &lt;span class="err"&gt;index&lt;/span&gt;  &lt;span class="err"&gt;index.html&lt;/span&gt; &lt;span class="err"&gt;index.htm&lt;/span&gt;&lt;span class="c"&gt;;
&lt;/span&gt;    &lt;span class="err"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;# Swagger is accessed via /swagger url. 
&lt;/span&gt;    &lt;span class="err"&gt;location&lt;/span&gt; &lt;span class="err"&gt;/swagger&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
    &lt;span class="err"&gt;proxy_pass&lt;/span&gt; &lt;span class="err"&gt;http://host.docker.internal:8080/swagger-ui&lt;/span&gt;&lt;span class="c"&gt;;
&lt;/span&gt;    &lt;span class="err"&gt;index&lt;/span&gt; &lt;span class="err"&gt;index.html&lt;/span&gt;&lt;span class="c"&gt;;
&lt;/span&gt;    &lt;span class="err"&gt;proxy_redirect&lt;/span&gt; &lt;span class="err"&gt;off&lt;/span&gt;&lt;span class="c"&gt;;
&lt;/span&gt;    &lt;span class="err"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;# This is also for Swagger that requires /v3/... url
&lt;/span&gt;    &lt;span class="err"&gt;location&lt;/span&gt; &lt;span class="err"&gt;/v3&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
        &lt;span class="err"&gt;proxy_pass&lt;/span&gt; &lt;span class="err"&gt;http://host.docker.internal:8080/v3&lt;/span&gt;&lt;span class="c"&gt;;
&lt;/span&gt;    &lt;span class="err"&gt;proxy_redirect&lt;/span&gt; &lt;span class="err"&gt;off&lt;/span&gt;&lt;span class="c"&gt;;
&lt;/span&gt;    &lt;span class="err"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;# This is the sump data endpoint.
&lt;/span&gt;    &lt;span class="err"&gt;location&lt;/span&gt; &lt;span class="err"&gt;/devices&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
        &lt;span class="err"&gt;proxy_pass&lt;/span&gt; &lt;span class="err"&gt;http://host.docker.internal:8080/devices&lt;/span&gt;&lt;span class="c"&gt;;
&lt;/span&gt;    &lt;span class="err"&gt;proxy_redirect&lt;/span&gt; &lt;span class="err"&gt;off&lt;/span&gt;&lt;span class="c"&gt;;
&lt;/span&gt;    &lt;span class="err"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;#error_page  404              /404.html;
&lt;/span&gt;
    &lt;span class="c"&gt;# redirect server error pages to the static page /50x.html
&lt;/span&gt;    &lt;span class="c"&gt;#
&lt;/span&gt;    &lt;span class="err"&gt;error_page&lt;/span&gt;   &lt;span class="err"&gt;500&lt;/span&gt; &lt;span class="err"&gt;502&lt;/span&gt; &lt;span class="err"&gt;503&lt;/span&gt; &lt;span class="err"&gt;504&lt;/span&gt;  &lt;span class="err"&gt;/50x.html&lt;/span&gt;&lt;span class="c"&gt;;
&lt;/span&gt;    &lt;span class="py"&gt;location&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;/50x.html {&lt;/span&gt;
        &lt;span class="err"&gt;root&lt;/span&gt;   &lt;span class="err"&gt;/usr/share/nginx/html&lt;/span&gt;&lt;span class="c"&gt;;
&lt;/span&gt;    &lt;span class="err"&gt;}&lt;/span&gt;

&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After editing the conf file, to test&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;nginx &lt;span class="nt"&gt;-t&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To reload the conf file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nginx &lt;span class="nt"&gt;-s&lt;/span&gt; reload
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Adding Streamlit
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://streamlit.io"&gt;Streamlit&lt;/a&gt; is a frontend web application to visualize data. For my project, I'm using it to draw a chart for the sump water level. &lt;/p&gt;

&lt;p&gt;I originally just set up the same way as sump data API endpoint. Not that the port 8501 is the Streamlit app port (locally, it can be accessed &lt;a href="http://localhost:8501"&gt;http://localhost:8501&lt;/a&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    location / {
        proxy_pass http://host.docker.internal:8501/;
        proxy_redirect off;
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unfortunately, this doesn't work. The reason is Streamlit uses WebSocket under &lt;code&gt;_stcore&lt;/code&gt; folder. To properly configure the reverse proxy, and HTTP Upgrade needs to be set.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    location /_stcore {
        proxy_pass http://host.docker.internal:8501/_stcore;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "Upgrade";
        proxy_set_header Host $host;
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reference: &lt;a href="https://www.nginx.com/blog/websocket-nginx/"&gt;NGINX as a WebSocket Proxy&lt;/a&gt;&lt;/p&gt;

</description>
      <category>docker</category>
      <category>nginx</category>
    </item>
    <item>
      <title>My Java Memo</title>
      <dc:creator>NT</dc:creator>
      <pubDate>Tue, 31 Oct 2023 01:14:59 +0000</pubDate>
      <link>https://dev.to/nobudev7/my-java-memo-159j</link>
      <guid>https://dev.to/nobudev7/my-java-memo-159j</guid>
      <description>&lt;p&gt;Various findings that would help me later.&lt;/p&gt;

&lt;h2&gt;
  
  
  java: cannot find symbol error
&lt;/h2&gt;

&lt;p&gt;For example &lt;code&gt;List#getFirst&lt;/code&gt; method is a default method of the interface that is implemented since version 21. This doesn't compile well in IntelliJ with default settings.&lt;/p&gt;

&lt;p&gt;To make the project allow compiling in Java 21, uncheck the &lt;code&gt;--release&lt;/code&gt; option, as the binary won't be compatible with older Java.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6-vyzmD3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h1m9u5xe61z79fswezrw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6-vyzmD3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h1m9u5xe61z79fswezrw.png" alt="Uncheck release option" width="800" height="445"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Installing Streamlit on Mac</title>
      <dc:creator>NT</dc:creator>
      <pubDate>Sat, 28 Oct 2023 13:21:40 +0000</pubDate>
      <link>https://dev.to/nobudev7/streamlit-memo-cc1</link>
      <guid>https://dev.to/nobudev7/streamlit-memo-cc1</guid>
      <description>&lt;h2&gt;
  
  
  Python
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;python3
&lt;span class="nv"&gt;$ &lt;/span&gt;python3 &lt;span class="nt"&gt;--version&lt;/span&gt;
Python 3.9.6
&lt;span class="nv"&gt;$ &lt;/span&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--upgrade&lt;/span&gt; pip
&lt;span class="nv"&gt;$ &lt;/span&gt;pip3 &lt;span class="nt"&gt;-V&lt;/span&gt;
pip 23.2.1 from /opt/homebrew/lib/python3.11/site-packages/pip &lt;span class="o"&gt;(&lt;/span&gt;python 3.11&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Venv
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; venv venv
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;source &lt;/span&gt;venv/bin/activate
&lt;span class="o"&gt;(&lt;/span&gt;venv&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$ &lt;/span&gt;pip &lt;span class="nt"&gt;-V&lt;/span&gt;
pip 21.2.4 from /Users/nobu/Playground/st-app/venv/lib/python3.9/site-packages/pip &lt;span class="o"&gt;(&lt;/span&gt;python 3.9&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;venv&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$ &lt;/span&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--upgrade&lt;/span&gt; pip
&lt;span class="o"&gt;(&lt;/span&gt;venv&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$ &lt;/span&gt;pip &lt;span class="nt"&gt;-V&lt;/span&gt;                              
pip 23.3.1 from /Users/nobu/Playground/st-app/venv/lib/python3.9/site-packages/pip &lt;span class="o"&gt;(&lt;/span&gt;python 3.9&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;venv&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$ &lt;/span&gt;deactivate
&lt;span class="nv"&gt;$ &lt;/span&gt; pip &lt;span class="nt"&gt;-V&lt;/span&gt;    
zsh: &lt;span class="nb"&gt;command &lt;/span&gt;not found: pip
&lt;span class="nv"&gt;$ &lt;/span&gt;pip3 &lt;span class="nt"&gt;-V&lt;/span&gt;
pip 23.2.1 from /opt/homebrew/lib/python3.11/site-packages/pip &lt;span class="o"&gt;(&lt;/span&gt;python 3.11&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Install Streamlit
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;(&lt;/span&gt;venv&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$ &lt;/span&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;streamlit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  App
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;app.py&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;streamlit&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt;

&lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Title of the app&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;The App!&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subheader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;The future of SumpPump&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Run the app
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;(&lt;/span&gt;venv&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$ &lt;/span&gt;streamlit run app.py 
  You can now view your Streamlit app &lt;span class="k"&gt;in &lt;/span&gt;your browser.

  Local URL: http://localhost:8501
  Network URL: http://192.168.1.169:8501
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--y4ttIQQs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yqd6ihdv09cwfsjptjk1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--y4ttIQQs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yqd6ihdv09cwfsjptjk1.png" alt="Streamlit app" width="800" height="762"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Spring Boot: Multiple Primary Keys via JPA</title>
      <dc:creator>NT</dc:creator>
      <pubDate>Wed, 18 Oct 2023 02:38:27 +0000</pubDate>
      <link>https://dev.to/nobudev7/spring-boot-multiple-primary-keys-via-jpa-3489</link>
      <guid>https://dev.to/nobudev7/spring-boot-multiple-primary-keys-via-jpa-3489</guid>
      <description>&lt;h2&gt;
  
  
  Goal
&lt;/h2&gt;

&lt;p&gt;I wanted to have a simple database record with 2 primary keys in certain order, with a field that contains a simple value.&lt;/p&gt;

&lt;h2&gt;
  
  
  Java Persistent API with Spring
&lt;/h2&gt;

&lt;p&gt;Using Java Persistent API (JPA), I thought that this would automatically set up primary keys in the order in the class, but this doesn't work the way intended.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Entity&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DataEntry&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Id&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Integer&lt;/span&gt; &lt;span class="n"&gt;deviceID&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="nd"&gt;@Id&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;LocalDateTime&lt;/span&gt; &lt;span class="n"&gt;measuredOn&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Integer&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;/// getters and setters&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looking at this web page, I figured I need an ID Class.&lt;br&gt;
&lt;a href="https://www.baeldung.com/jpa-composite-primary-keys"&gt;https://www.baeldung.com/jpa-composite-primary-keys&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DataEntryId&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;Serializable&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Integer&lt;/span&gt; &lt;span class="n"&gt;deviceID&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;LocalDateTime&lt;/span&gt; &lt;span class="n"&gt;measuredOn&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;DataEntryId&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Integer&lt;/span&gt; &lt;span class="n"&gt;deviceID&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;LocalDateTime&lt;/span&gt; &lt;span class="n"&gt;measuredOn&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;deviceID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;deviceID&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;measuredOn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;measuredOn&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;/// equals and hashCode&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the ID Class defined, now that I can use it as an ID class in the annotation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Entity&lt;/span&gt;
&lt;span class="nd"&gt;@IdClass&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DataEntryId&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DataEntry&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="c1"&gt;///....&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running the Spring Boot app with the above would create a table structure correctly when the app started.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;mysql&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;desc&lt;/span&gt; &lt;span class="n"&gt;data_entry&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;-------------+-------------+------+-----+---------+-------+&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Field&lt;/span&gt;       &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;Type&lt;/span&gt;        &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;Null&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;Key&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;Default&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Extra&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;-------------+-------------+------+-----+---------+-------+&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;deviceid&lt;/span&gt;    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;         &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;NO&lt;/span&gt;   &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;PRI&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;    &lt;span class="o"&gt;|&lt;/span&gt;       &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;measured_on&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;NO&lt;/span&gt;   &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;PRI&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;    &lt;span class="o"&gt;|&lt;/span&gt;       &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;       &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;         &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;YES&lt;/span&gt;  &lt;span class="o"&gt;|&lt;/span&gt;     &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;    &lt;span class="o"&gt;|&lt;/span&gt;       &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;-------------+-------------+------+-----+---------+-------+&lt;/span&gt;

&lt;span class="n"&gt;mysql&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;show&lt;/span&gt; &lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt; &lt;span class="n"&gt;data_entry&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;data_entry&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="nv"&gt;`data_entry`&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nv"&gt;`deviceid`&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nv"&gt;`measured_on`&lt;/span&gt; &lt;span class="nb"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nv"&gt;`value`&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;`deviceid`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;`measured_on`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;ENGINE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;InnoDB&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="n"&gt;CHARSET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;utf8mb4&lt;/span&gt; &lt;span class="k"&gt;COLLATE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;utf8mb4_0900_ai_ci&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Problem
&lt;/h2&gt;

&lt;p&gt;However, in trying to store a data entry to this table, the following code generate an exception.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;        &lt;span class="nc"&gt;DataEntry&lt;/span&gt; &lt;span class="n"&gt;dataEntry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DataEntry&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;dataEntry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setDeviceID&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;deviceId&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;dataEntry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setMeasuredOn&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;measuredOn&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;dataEntry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setValue&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;dataEntryRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dataEntry&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Error:&lt;br&gt;
&lt;code&gt;org.hibernate.InstantiationException: Unable to locate constructor for embeddable : com.example.AccessData.DataEntryId&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;p&gt;A subtle requirement in the ID class is that it has to have a default (no arg) constructor.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;DataEntryId&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
    </item>
    <item>
      <title>Docker Memo -MySQL</title>
      <dc:creator>NT</dc:creator>
      <pubDate>Sun, 15 Oct 2023 20:43:14 +0000</pubDate>
      <link>https://dev.to/nobudev7/docker-memo-mysql-3hoi</link>
      <guid>https://dev.to/nobudev7/docker-memo-mysql-3hoi</guid>
      <description>&lt;h2&gt;
  
  
  Goal
&lt;/h2&gt;

&lt;p&gt;I just need a MySQL instance for development purpose, but with persisted data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Storage
&lt;/h2&gt;

&lt;p&gt;There are 2 ways to persist MySQL data, &lt;a href="https://docs.docker.com/storage/volumes/"&gt;docker volume&lt;/a&gt;, or mount a directory on the host machine. For my purpose, I use docker volume.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create Volume
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker volume create sumpdatavolume
&lt;span class="nv"&gt;$ &lt;/span&gt;docker volume &lt;span class="nb"&gt;ls&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;... snip ...&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;local     &lt;/span&gt;sumpdatavolume
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Run MySQL
&lt;/h3&gt;

&lt;p&gt;Run MySQL, create a test db and table.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;--name&lt;/span&gt; sumpdatamysql &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 3306:3306 &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;MYSQL_ROOT_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;myrootpswd &lt;span class="nt"&gt;-v&lt;/span&gt; sumpdatavolume:/var/lib/mysql mysql
&lt;span class="nv"&gt;$ &lt;/span&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; sumpdatamysql bash
bash-4.4# mysql &lt;span class="nt"&gt;-pmyrootpswd&lt;/span&gt;
mysql&amp;gt; create database &lt;span class="nb"&gt;test&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
use &lt;span class="nb"&gt;test&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
create table &lt;span class="nb"&gt;test&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id &lt;/span&gt;int, name varchar&lt;span class="o"&gt;(&lt;/span&gt;10&lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
insert into &lt;span class="nb"&gt;test&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;, name&lt;span class="o"&gt;)&lt;/span&gt; values &lt;span class="o"&gt;(&lt;/span&gt;1,&lt;span class="s2"&gt;"John"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;, &lt;span class="o"&gt;(&lt;/span&gt;2, &lt;span class="s2"&gt;"Mike"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; from &lt;span class="nb"&gt;test&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
+------+------+
| &lt;span class="nb"&gt;id&lt;/span&gt;   | name |
+------+------+
|    1 | John |
|    2 | Mike |
+------+------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Check persistent data
&lt;/h3&gt;

&lt;p&gt;Remove the existing container and recreate MySQL using the same docker volume.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker stop sumpdatamysql
&lt;span class="nv"&gt;$ &lt;/span&gt;docker &lt;span class="nb"&gt;rm &lt;/span&gt;sumpdatamysql
&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;--name&lt;/span&gt; sumpdatamysql &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 3306:3306 &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;MYSQL_ROOT_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;myrootpswd &lt;span class="nt"&gt;-v&lt;/span&gt; sumpdatavolume:/var/lib/mysql mysql
&lt;span class="nv"&gt;$ &lt;/span&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; sumpdatamysql bash
mysql &lt;span class="nt"&gt;-pmyrootpswd&lt;/span&gt;
use &lt;span class="nb"&gt;test&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; from &lt;span class="nb"&gt;test&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
+------+------+
| &lt;span class="nb"&gt;id&lt;/span&gt;   | name |
+------+------+
|    1 | John |
|    2 | Mike |
+------+------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As shown above in &lt;code&gt;select&lt;/code&gt; statement, the same data is there.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Run a SpringBoot jar on Docker</title>
      <dc:creator>NT</dc:creator>
      <pubDate>Tue, 22 Aug 2023 00:58:18 +0000</pubDate>
      <link>https://dev.to/nobudev7/run-a-springboot-jar-on-docker-1fgd</link>
      <guid>https://dev.to/nobudev7/run-a-springboot-jar-on-docker-1fgd</guid>
      <description>&lt;p&gt;Some articles suggest to run a centos docker image to run SpringBoot app. Since CentOS is deprecated, I found that &lt;a href="https://hub.docker.com/_/amazoncorretto"&gt;amazoncorretto&lt;/a&gt; is an option.&lt;/p&gt;

&lt;p&gt;Dockerfile&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; amazoncorretto:20&lt;/span&gt;

&lt;span class="k"&gt;VOLUME&lt;/span&gt;&lt;span class="s"&gt; [ "/data" ]&lt;/span&gt;
&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="s"&gt; MySpringBootApp-0.0.1-SNAPSHOT.jar MySpringBootApp&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'touch /ValidatingFormInput.jar'&lt;/span&gt;
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; [ "java", "-jar", "/MySpringBootApp" ]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Build Docker image&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker build -t myspringbootapp .  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that the Docker image name has to be lower case.&lt;/p&gt;

&lt;p&gt;Run the docker container&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:8080 myspringbootapp

  &lt;span class="nb"&gt;.&lt;/span&gt;   ____          _            __ _ _
 /&lt;span class="se"&gt;\\&lt;/span&gt; / ___&lt;span class="s1"&gt;'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '&lt;/span&gt;_ | &lt;span class="s1"&gt;'_| | '&lt;/span&gt;_ &lt;span class="se"&gt;\/&lt;/span&gt; _&lt;span class="sb"&gt;`&lt;/span&gt; | &lt;span class="se"&gt;\ \ \ \&lt;/span&gt;
 &lt;span class="se"&gt;\\&lt;/span&gt;/  ___&lt;span class="o"&gt;)&lt;/span&gt;| |_&lt;span class="o"&gt;)&lt;/span&gt;| | | | | &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;_| |  &lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="s1"&gt;'  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v3.1.1)

2023-08-22T00:41:04.306Z  INFO 1 --- [           main] i.s.s.V.MySpringBootAppApplication   : Starting MySpringBootAppApplication v0.0.1-SNAPSHOT using Java 20.0.2 with PID 1 (/MySpringBootAppApplication.jar started by root in /)
2023-08-22T00:41:04.307Z  INFO 1 --- [           main] i.s.s.V.MySpringBootAppApplication   : No active profile set, falling back to 1 default profile: "default"
2023-08-22T00:41:04.800Z  INFO 1 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2023-08-22T00:41:04.805Z  INFO 1 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2023-08-22T00:41:04.805Z  INFO 1 --- [           main] o.apache.catalina.core.StandardEngine    : Starting Servlet engine: [Apache Tomcat/10.1.10]
2023-08-22T00:41:04.838Z  INFO 1 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2023-08-22T00:41:04.838Z  INFO 1 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 505 ms
2023-08-22T00:41:05.006Z  INFO 1 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2023-08-22T00:41:05.013Z  INFO 1 --- [           main] i.s.s.V.MySpringBootAppApplication   : Started MySpringBootAppApplication in 0.942 seconds (process running for 1.172)
2023-08-22T00:41:18.153Z  INFO 1 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet '&lt;/span&gt;dispatcherServlet&lt;span class="s1"&gt;'
2023-08-22T00:41:18.153Z  INFO 1 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet '&lt;/span&gt;dispatcherServlet&lt;span class="s1"&gt;'
2023-08-22T00:41:18.154Z  INFO 1 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 1 ms

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>docker</category>
    </item>
    <item>
      <title>Set up React on Mac</title>
      <dc:creator>NT</dc:creator>
      <pubDate>Tue, 20 Jun 2023 00:40:04 +0000</pubDate>
      <link>https://dev.to/nobudev7/set-up-react-on-mac-2ed0</link>
      <guid>https://dev.to/nobudev7/set-up-react-on-mac-2ed0</guid>
      <description>&lt;h2&gt;
  
  
  Install Node.js
&lt;/h2&gt;

&lt;p&gt;Check &lt;code&gt;brew&lt;/code&gt; version.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;brew &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;span class="go"&gt;Homebrew 4.0.23
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Install nodebrew
&lt;/h2&gt;

&lt;p&gt;Following this post, I decided to install nodebrew. I won't be an active frontend developer, but this might help in the future to switch or maintain versions of &lt;code&gt;npm&lt;/code&gt; as described.&lt;br&gt;
&lt;a href="https://fromscratch-y.work/en/blog/programming/mac-nodejs-install/#sec4-1"&gt;https://fromscratch-y.work/en/blog/programming/mac-nodejs-install/#sec4-1&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;nodebrew
&lt;span class="go"&gt;(...snip...)
&lt;/span&gt;&lt;span class="gp"&gt;==&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Caveats
&lt;span class="go"&gt;You need to manually run setup_dirs to create directories required by nodebrew:
  /opt/homebrew/opt/nodebrew/bin/nodebrew setup_dirs

Add path:
&lt;/span&gt;&lt;span class="gp"&gt;  export PATH=$&lt;/span&gt;HOME/.nodebrew/current/bin:&lt;span class="nv"&gt;$PATH&lt;/span&gt;
&lt;span class="go"&gt;
To use Homebrew's directories rather than ~/.nodebrew add to your profile:
  export NODEBREW_ROOT=/opt/homebrew/var/nodebrew

zsh completions have been installed to:
  /opt/homebrew/share/zsh/site-functions
&lt;/span&gt;&lt;span class="gp"&gt;==&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Summary
&lt;span class="go"&gt;🍺  /opt/homebrew/Cellar/nodebrew/1.2.0: 8 files, 40.6KB

(...snip...)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not sure what the &lt;code&gt;setup_dirs&lt;/code&gt; does...&lt;/p&gt;

&lt;p&gt;Added the PATH to my &lt;code&gt;.zshrc&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'export PATH=$HOME/.nodebrew/current/bin:$PATH'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; ~/.zshrc 
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;source&lt;/span&gt; ~/.zshrc 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Specify the node version
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nodebrew ls-remote
&lt;span class="go"&gt;v0.0.1    v0.0.2    v0.0.3    v0.0.4    v0.0.5    v0.0.6    

v0.1.0    v0.1.1    v0.1.2    v0.1.3    v0.1.4    v0.1.5    v0.1.6    v0.1.7
(...snip...)

v20.0.0   v20.1.0   v20.2.0   v20.3.0   
(...snip...)

io@v3.0.0 io@v3.1.0 io@v3.2.0 io@v3.3.0 io@v3.3.1 
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install the stable version&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nodebrew &lt;span class="nb"&gt;install &lt;/span&gt;stable
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nodebrew list
&lt;span class="go"&gt;v20.3.0
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Activate the installed version&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nodebrew use v20.3.0
&lt;span class="go"&gt;use v20.3.0
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;node &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;span class="go"&gt;v20.3.0
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;npm &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;span class="go"&gt;9.6.7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Create an app
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;npx create-react-app react-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;According to this page &lt;a href="https://react.dev/learn/start-a-new-react-project"&gt;https://react.dev/learn/start-a-new-react-project&lt;/a&gt;, that creates &lt;a href="https://nextjs.org"&gt;Next.js&lt;/a&gt; project.&lt;/p&gt;

&lt;p&gt;To run, go down to the created project folder, and start &lt;code&gt;npm&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;react-app
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This automatically opens &lt;code&gt;http://localhost:3000&lt;/code&gt; with your default browser and show the starter page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YglgYYp8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wtnltxfs22psabdz0zvs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YglgYYp8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wtnltxfs22psabdz0zvs.png" alt="React Starter Page" width="800" height="795"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Further more, below comment will create a &lt;code&gt;build&lt;/code&gt; folder for deployment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;npm run build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Source: &lt;a href="https://create-react-app.dev/docs/deployment/"&gt;https://create-react-app.dev/docs/deployment/&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;My plan it to run a server using Spring Boot.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why React?
&lt;/h2&gt;

&lt;p&gt;As a Java backend developer, I need to work with frontend developers. Also wanted to set up a simple website based on Java backend, and picked React as the FE framework - simply because it's most trending framework on the FE side. &lt;/p&gt;

</description>
    </item>
    <item>
      <title>Setting up IntelliJ Community Edition</title>
      <dc:creator>NT</dc:creator>
      <pubDate>Sat, 17 Jun 2023 22:27:32 +0000</pubDate>
      <link>https://dev.to/nobudev7/setting-up-intellij-community-edition-3a5o</link>
      <guid>https://dev.to/nobudev7/setting-up-intellij-community-edition-3a5o</guid>
      <description>&lt;h2&gt;
  
  
  Install JDK
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;openjdk
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo ln&lt;/span&gt; &lt;span class="nt"&gt;-sfn&lt;/span&gt; /opt/homebrew/opt/openjdk/libexec/openjdk.jdk /Library/Java/JavaVirtualMachines/openjdk.jdk
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'export PATH="/opt/homebrew/opt/openjdk/bin:$PATH"'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; ~/.zshrc
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'export CPPFLAGS="-I/opt/homebrew/opt/openjdk/include"'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; ~/.zshrc
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;source&lt;/span&gt; ~/.zshrc
&lt;span class="nv"&gt;$ &lt;/span&gt;java &lt;span class="nt"&gt;--version&lt;/span&gt;
openjdk 20.0.1 2023-04-18
OpenJDK Runtime Environment Homebrew &lt;span class="o"&gt;(&lt;/span&gt;build 20.0.1&lt;span class="o"&gt;)&lt;/span&gt;
OpenJDK 64-Bit Server VM Homebrew &lt;span class="o"&gt;(&lt;/span&gt;build 20.0.1, mixed mode, sharing&lt;span class="o"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Install IntelliJ Community Edition
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.jetbrains.com/idea/download/?var=1&amp;amp;section=mac"&gt;https://www.jetbrains.com/idea/download/?var=1&amp;amp;section=mac&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Spring Boot Project
&lt;/h2&gt;

&lt;p&gt;Use initializer to create a new project&lt;/p&gt;

&lt;p&gt;&lt;a href="https://start.spring.io"&gt;https://start.spring.io&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Select Maven&lt;/li&gt;
&lt;li&gt;Add Spring Web dependency&lt;/li&gt;
&lt;li&gt;Select the correct Java version&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JiLRnSjS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2rw5996u4rxx1aw1kzy1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JiLRnSjS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2rw5996u4rxx1aw1kzy1.png" alt="Select the correct Java version" width="800" height="569"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Open the project in IntelliJ CE&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Trust the project&lt;/li&gt;
&lt;li&gt;Specify JDK in Project Structure
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5FfxQvoU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kdtgwv3xnwnb5xua4v0k.png" alt="Specify JDK in Project Structure" width="800" height="367"&gt;
&lt;/li&gt;
&lt;li&gt;Add a controller
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.example.demo&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.springframework.web.bind.annotation.RequestMapping&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.springframework.web.bind.annotation.RestController&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@RestController&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HelloWorldController&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@RequestMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;hello&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"Hello Spring Boot!"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Build project&lt;/li&gt;
&lt;li&gt;Run the project&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JVfRWyxn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pv01m8nadbddudf713pv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JVfRWyxn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pv01m8nadbddudf713pv.png" alt="Run the project" width="800" height="422"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open a browser and point to &lt;code&gt;localhost:8080&lt;/code&gt; to verify the web application is running&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jgi4jrba--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7q7chnksd0549j8805ov.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jgi4jrba--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7q7chnksd0549j8805ov.png" alt="Hello Spring App Running" width="800" height="372"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
