Chris Padilla/Blog
My passion project! Posts spanning music, art, software, books, and more. Equal parts journal, sketchbook, mixtape, dev diary, and commonplace book.
- The Controller layer is responsible for connecting our data from the service layer with a means of rendering or presentation.
- The Model layer handles communication between our application and external database.
- The Repositories layer is responsible for querying logic to our database. If the model layer is establishing communication with the database, then the repositories layer is handling what type of requests we're making available to our application.
- The Service layer handles all the business logic for our application. This layer relies on the repositories layer to provide the data so that it then can be transformed in whatever way we need it to be before delivery.
- Spring Boot runs on port
8080
by default. In my configurationapplication.properties
, I've set the port to80
. This is the default HTTP port and makes it so that, on the EC2 server, I'll be able to access the app. Otherwise, instead of "myapp.com", I would have to access "myapp.com:8080". To match both within and without the container, I'm setting the port config. - I'm setting my environment variables on both. The default port for PostgreSQL is
5432
, so that's where the db url points to. - Hibernate is an ORM for Java objects to SQL/relational databases. Here I'm specifying that Hibernate should update the SQL schema based on my applications model configuration.
- Ensure you have a VPC created. The default is fine if you have it.
- Instantiate your EC2, configured to Linux.
- Generate your key pair
- Edit the security group to allow inbound HTTP requests
- Docker
- Docker Compose
- Git
- Maven (or whichever build tool you are using)
- Add the current user to docker:
sudo usermod -aG docker $USER sudo reboot
- Clone your git repo to the server (prereq: Upload your project to GitHub!)
git clone ssh://john@example.com/path/to/my-project.git
- Build the application locally
mvn package
- We'll have to move the jar file to the docker directory once again.
- Navigate to the docker directory.
cd src/main/docker
- Build the docker image
docker-compose -f docker-compose.yml build
- Run the container with
docker-compose up
ordocker-compose up -d
to run in the background and keep it running after you exit the server. - Git push changes
- SSH back into the server
- Clone the repo
- Rebuild the executable
- Rebuild the docker image
- Rerun the docker container
The Haps - May 2024
It's summer time and I'm feeling inspired to spin my "now" page back up!
Software
I've been in software for 3 years now! Still enjoying my time at AptAmigo.
So far, I've commited to learning a new programming language every year. This year, I've been having fun getting familiar with Java. The type system paired with VS Code intellisense is lovely.
Now having had a few years of experience working professionally, I've really taken a liking to sharpening my thinking by writing short tech articles. It's a great space to share what I'm working on and find clear ways of explaining it. My favorite part is when someone comes across a problem I've faced already. I can reach back to my own post to help them through the solution!
You can keep up with my tech articles through the tech tag on my blog. You can also find me on LinkedIn.
Music

I'm bouncing between playing guitar, piano, and writing my own music. To paraphrase W.A. Mathieu, it's fun to have many pots boiling!
After decades of communal music making and monophonic sound on the saxophone, I'm thrilled to be exploring independent and polyphonic instruments.
To make things even more interesting, I'm putting most of my focus on learning jazz on piano and guitar. A friend of mine told me that it's actually the way to go β to learn the vocabulary harmonically as opposed to in a scalar way.
For music writing, it's been fun to keep it loose! I take what ever I'm listening to at the moment, deconstruct it, and turn it into something new. It's a wildly fun outlet!
You can browse my mini albums here. You can keep up with my guitar and piano playing through the music tag on my blog. I'm also sharing recordings on Instagram.
Art
A couple of years ago I dedicated myself to learning how to draw and create digital paintings! I used to make my own comics growing up, but I always assumed my sister had all the talent. After switching from music to code, I realized I could learn anything. So I may as well learn a skill I've only ever dreamed of having! π
I'm spending time balancing the fundamentals with just fooling around. This year has been all about figure drawing, direct painting, and making silly little digital sketches. The best part is that I have an excuse to revisit cartoons, comics, manga, and video games as sources of inspiration!
You can see what I've made so far through the Art tag on my blog. I'm also sharing drawings on Instagram.
Dallas
Living in Dallas with Miranda and our dog Lucy! We moved from Austin a couple of years ago so Miranda could attend Parker University for their Doctor of Chiropractic program. Only a year left as of this writing, then we'll see where we end up next!
So far it's been my favorite metro area in Texas to live in. Downtown is just a quick drive from where we are. But, in the other direction, artsy Denton is not far either.
All good things!
π
Billy Strayhorn β Take the "A" Train
π An oldie but a goodie
Noodle Bird Cover Redux
My original album cover for Noodle Bird was pretty crunchy. π So I whipped up a new rendition!
Two Years of Blogging
From Austin Kleon:
The way I show up for myself, the way I discover who I really am, is to make an appointment every day to show up to the page. If I show up to the page, I show up to myself.
Two years of blogging! A small part of many years of creating and making, and it's certainly worth marking the occasion.
Last year I shared my Lessons From a Year of Blogging. I'd largely echo those points this year. I still enjoy the clarity of thought that comes from writing and making, the ability to carry a long term conversation while leaving a trail, and having a stage to perform on.
One other thing I've come to appreciate with blogging as a medium is this:
It's Malleable
I took a moment to look back at what I've made this year. For context: This started largely as a text blog, mostly developer logs. Out of the soil, buds of more literary posts on what I was reading began to sprout. Soon after that, I started sharing art and music.
I've since traded words for color and sound! Lately I only write for my tech posts. And the switch is intentional β I want to get more miles out of music and drawing, and there are only so many hours in the day.
When I started, a was wringing my hands trying to decide what one thing I would blog about. What a relief that it doesn't have to be one niche!
What I admire in the blogs I read is the spectrum of humanity that shines through. There's no predefined formula, and no algorithm to juice if you don't want to. So I end up seeing people as polyhedrons through the diversity they share and make.
On top of that, tomorrow I could decide that I'm all in on fly fishing. There's no barrier to me making this blog all about that for a couple of years. And I would revel in being able to look back fondly on the different phases and eras.
Starting out, I was worried about the idea that, if no one read what I wrote, I'd be doing all this for nothing. Turns out the rewards that come from a creative act alone absolutely eclipse any hit to the ego I would take if a post gets overlooked!
Elizabeth Gilbert in Big Magic:
You might end up dancing with royalty. Or you might just end up having to dance alone in the corner of the castle with your big, ungainly red foam claws waving in the empty air. that's fine, too. Sometimes it's like that. What you absolutely must not do is turn around and walk out. Otherwise, you will miss the party, and that would be a pity, because - please believe me - we did not come all this great distance, and make all this great effort, only to miss the party at the last moment.
And, of course, even if my inbox doesn't get flooded with mail on the eureka moment someone had reading a tech blog post, writing publicly is still a phenomenal tool for clarifying thinking in that craft.
I'm thinking much about the question offered through James Hollis' work: "What is seeking expression through me?" The wonderful thing about a personal blog is an opportunity to flexibly celebrate that expression.
The generous part of that showing up is that, by doing so, we then bring our fullest selves to the people we serve.
So start with where ever you are! Bring all parts of yourself along for the ride. You may find, as I have, it's an excellent way to explore the answers to that question.
Layers in a Java Spring Boot API
Spring Boot is a well embraced and powerful framework for developing web applications. Getting a backend API setup is fairly quick and allows for a great deal of durability up front. A fine balance between moving quickly on an app while giving you the guard rails to maintain organization, scalability, and durability.
Here's the quick-start guide to getting an API setup:
Layers
If you're used to the MVC pattern in software development, you'll find a similar intent of organization around Spring Boot Architecture. However, the categories we group our logic in will be slightly different.
In Spring Boot, these categories are referred to as layers of the application. They have their own directories and are bundled in their own package within the application.
Here's a simple directory structure within the src
folder of our app:
project/src/main/java/com/projectname
|-- controller
| |-- ArtistController.java
|-- model
| |-- Artist.java
|-- repositories
| |-- ArtistRepository.java
+-- service
|-- ArtistService.java
Note that tests will live in their own directory outside of the src
folder.
Above, all layers are represented:
In Action
Ok! So say that I have my base app setup for a Spotify Popularity Monitor API. From here, I want to setup a feature for each layer that supports CRUD operations for an Artist
.
I'll start with the model:
Model
package com.chrispadilla.spotifyperformancemonitor.model;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
@Entity
public class Artist {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@Column
private String name;
@Column
private String[] genres;
@Column
private String spotifyId;
@Column
private String spotifyUrl;
// Getters/Setters here
}
For brevity, I'm leaving out the Getters and Setters. What I'm showing is the schema for an Artist as I want it setup in my relational DB.
By adding @Entity
, I'm denoting this as a persistence layer class for the JPA.
Next, I'll add the repository:
Repository
package com.chrispadilla.spotifyperformancemonitor.repositories;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import com.chrispadilla.spotifyperformancemonitor.model.Artist;
@Repository
public interface ArtistRepository extends CrudRepository<Artist, Long>{
Artist findByName(String name);
Artist findBySpotifyId(String spotifyId);
}
Fairly simple! Spring Repositories already come with several CRUD methods. I enable them by extending from CrudRepository
. By default, I'll be able to call findById
, but if I want to query by another field, I specify it on the interface with a method. I've done this with findByName
and findBySpotifyId
above. Spring will automatically make the connection that if I'm passing in a variable spotifyId
, that this is what I'm querying by on the model. No extra logic required!
Service
The service layer will largely depend on what I'm trying to do with the data. It goes beyond the scope of today's blog, so here's the bare bones setup for a service class:
package com.chrispadilla.spotifyperformancemonitor.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.chrispadilla.spotifyperformancemonitor.model.Artist;
@Service
public class ArtistService implements IArtistService {
@Autowired
private ArtistRepository artistRepository;
public void collectArtist(String spotifyId) {
// Do your stuff
}
}
It's good practice to create an interface outlining the methods you'll implement, so here I'm extending from an IArtistService
instance above.
Again, an annotation of @Service
marks this as a bean that can be injected throughout my other layers.
Controller
It all comes together in the controller! Here I'll setup a controller to GET and POST artists:
package com.chrispadilla.spotifyperformancemonitor.controller;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.server.ResponseStatusException;
import com.chrispadilla.spotifyperformancemonitor.model.Artist;
import com.chrispadilla.spotifyperformancemonitor.repositories.ArtistRepository;
import com.chrispadilla.spotifyperformancemonitor.service.ArtistService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
@RestController
@RequestMapping("/api/artist")
public class ArtistController {
@Autowired
private ArtistRepository artistRepository;
@Autowired
private ArtistService artistService;
@GetMapping
@ResponseStatus(HttpStatus.OK)
public Iterable<Artist> getArtists() {
return artistRepository.findAll();
}
@GetMapping("/{id}")
public Artist getArtist(@PathVariable Long id) {
Artist artist = artistRepository.findById(id)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "No matching ID"));
return artist;
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Artist postArtist(@RequestBody Artist artist) {
return artistRepository.save(artist);
}
}
Several nice annotations are made available here! RequestMapping
, GetMapping
, and PostMapping
allow us to define the path for our controller. PathVariable
gives access to the id
that's listed as part of my path in my get request.
Here, you'll see I'm using Autowired
to tell Spring to inject an instance of my repository and service layer classes created above. I can then use them freely within my controller.
It can be tempting to write the business logic directly in the controller. To maintain flexibility, though, it's best to leave minimal db logic in the controller and wrap up data transformation within a method on your service layer classes. This allows for a more highly decoupled system. Were I to add a Presentation layer, it would be far simpler to access the service layer object, as opposed to refactoring a bloated controller method.
That's It!
With just a few files written, we have a strong basis for a flexible and easily extendable API! All with type checking along the way and a largely decoupled system! π
Minako Adachi β Iki Town from Pokemon Sun & Moon
Summer is coming! Only felt appropriate to play some Pokemon island music π
Duck Fam
Polymorphism in Java
Of the core pillars of Object Oriented Programming, Polymorphism affords a great deal of flexibility. While inheritance allows for encapsulated and abstracted data to be shared, polymorphism allows for the redefining of both the data and the attached methods based on context.
Java supports polymorphic behavior through two means: Method Overloading and Method Overriding.
Demonstration
Say that I have an ArchiveItem
class that stores the basic details of a name and id. Here I'll demonstrate both overloading and overriding:
package polymorphism;
public class ArchiveItem {
private String id;
private String name;
// Overloaded constructor with all instance variables supplied
public ArchiveItem(String id, String name) {
this.id = id;
this.name = name;
}
// alternative constructor with only the name provided
public ArchiveItem(String name) {
this.name = name;
this.id = IDPackage.generate();
}
// Override the base equals() method
@Override
public boolean equals(Object compared) {
if (compared == this) return true;
if (!(compared instanceof ArchiveItem)) return false;
ArchiveItem comparedItem = (ArchiveItem) compared;
return this.id.equals(comparedItem.id);
}
// Override the base toString() method
@Override
public String toString() {
return String.format("%s: %s", this.id, this.name);
}
}
Taking a look at my constructors, I'm overloading the ArchiveItem
method by declaring the same method, but with a different number of arguments both times. When instantiated, Java will know which of the two methods to run based on what arguments are provided. Another way of putting it: One of the two methods will be called depending on their signature.
Looking further down, I've written an equals
and a toString
method. All Objects in Java come with these methods. Every class inherits from the base Java Object class, and on that class are implementations for equals
and toString
. In fact, toString
is what's called anytime you print an object to the console.
Without any adjustments, passing an object to System.out.println()
would return something like this:
ArchiveItem guitar = new ArchiveItem("Guitar");
System.out.print(guitar);
// "polymorphism.ArchiveItem@28d93b30"
The base print method will print the classname to the left of the @
symbol and the location in memory to the right. Typically, we want something more descriptive representing our class instance.
In the example above, I'm pringint instead the provided id and name of the ArchiveItem
By adding the @Override
annotation, I'm declaring that I'm intending to implement my own logic for the already inherited toString
method. The @Override
annotation is actually not necessary, but recommended. This will flag to the compiler to check that you're in fact overriding an existing method. Great for catching typos!
Putting It All Together
ArchiveItem piano = new ArchiveItem("Piano");
System.out.print(piano);
// "Piano, 93nkf903f"
Here it is in action! The ArchiveItem
is instantiated with only one argument, so it calls the matching method. One line down, I'm calling my implementation of toString
by passing my piano object into the print method.
Here is the same class but with a different constructor signature:
ArchiveItem piano = new ArchiveItem("custom-id", "Piano");
System.out.print(piano);
// "Piano, custom-id"
Gwynn β Woodland Waltz
A sweet dance from one of my Mom's old piano books π¦
Blogs, Social Media, and Feedback
You should blog! I've shared my thoughts on why before. Here's my new favorite introduction to why you should do it, in Q&A style from Marc Weidenbaum over at Disquiet.
Here's my favorite bit on the difference between social media and blogging:
Q: Why would I blog if I get more feedback on social media than I do whenever Iβve blogged?
A: Itβs in the terminology: Social media is βsocial.β Blogs are βweb logs.β Social media expects feedback (not just comments, but likes and follows). Blogs are you getting your ideas down; feedback is a byproduct, not a goal.
Still Life
Deploying Docker Compose Application to AWS EC2
Many deployment platforms (Vercel, Heroku, Render) add a great amount of magic and convenience to the process of publishing a web app. But is it all that difficult to work without some of the tooling?
I wanted to find out. So this week I put on my DevOps hat and decided to get my hands dirty!
My aim was to take an app I had built, wrap it up along with a database into a docker container, and deploy to AWS. All without extra bells and whistles β No Fargate, no Elastic Beanstalk, no CI/CD integration just yet. Just a simple Linux server on EC2!
In this case, it's a Java Spring Boot app with a PostgreSQL db. Though, since it's wrapped up in docker compose, this post will apply to any containerized app.
Here we go!
Local Setup
Write the Dockerfile
Assuming I already have my app built, we'll write the docker file for it. I'm going to store it under src/main/docker
for organization. We'll also keep it pretty simple for the application:
FROM openjdk:17-oracle
COPY . /app
ENTRYPOINT ["java", "-jar", "app/app.jar"]
All that's happening here is I'm using the Java image for the version I'll build with. Then I'll copy the contents into the container. And lastly, I'll kick off the program with java -jar app/app.jar
Build the Executable
If you're not running Spring Boot, feel free to skip ahead! Here's how I'm setting up my executable:
To build my app, I'm going to run mvn clean package
. This will generate a jar file in my target folder. From there, I'll simply move it over to the docker directory with the linux command:
cp target/demo-0.0.1-SNAPSHOT.jar src/main/docker/app.jar
Write the Docker Compose Config
Next is the docker compose file. This is where I'm bringing in the PostgreSQL db and wrapping it up with my app. Here's the file:
services:
app:
container_name: spring-boot-postgresql
image: 'docker-spring-boot-postgres:latest'
build:
context: .
dockerfile: Dockerfile
ports:
- "80:80"
depends_on:
- db
environment:
- SPRING_DATASOURC_URL=jdbc:postgresql://db:5432/compose-postgres
- SPRING_DATASOURCE_USERNAME=compose-postgres
- SPRING_DATASOURCE_PASSWORD=compose-postgres
- SPRING_JPA_HIBERNATE_DDL_AUTO=update
db:
image: 'postgres:13.1-alpine'
container_name: db
environment:
- POSTGRES_USER=compose-postgres
- POSTGRES_PASSWORD=compose-postgres
app
and db
are the individual images here for my container. For each, I'm pulling the relevant dependency images for Spring and PostgreSQL respectively. Under app.build
We're setting the context to be the current director (src/main/docker
) and pulling the docker file from there.
A few areas specific to my setup:
AWS Setup
At this point, I'll point you to the AWS docs for setting up an EC2 instance. Here's the gist:
Once your EC2 is up, it's time to SSH into it!
SSH and Installs
From your local machine, grab your key pair as well as the public DNS addres. (You can find instructions on the instance page after clicking "connect")
ssh -i /main-keypair.pem ec2-user@ec2-34-75-385-24.compute-1.amazonaws.com
The most magical part to me: after that, you'll be logged in and accessing the Linux terminal on your server!!
Since it's simply a Linux server, we can install all the dependencies we need just as if we were doing it on our own machine.
From here, install:
After that, here's how we'll get the code onto our server:
After that, accessing the public DNS address should show your app up and running!
Automation
Now the app is up! However, what if we need to make changes to the app? It's not a back-breaking process, but it would involve a few steps:
Something that is certainly doable in a few minutes. But it screams for automation, doesn't it?
The next step for me would be to embrace that automation. Knowing the steps individually for deploying an app to a Linux server, I would be taking a look at tools such as GitHub Actions or CircleCI to automate much of this process.
Then, of course, there are many more considerations for a real world app. Performance monitoring, error logging, automatic scaling, load balancing β just to name a few!
It was great to take a deep dive on deployment in isolation! On to exploring further tooling to support that process.
White Coat
Blog Post Syntax Highlighting
I've added syntax highlighting to the blog! Long overdue. Here's how I made it happen:
Setup
This site is a Next.js app. The blog posts are generated with the built in Static Site Generation feature. For each post, I grab all the urls to render and then they are constructed at build time:
import AlbumPage from '/components/albumPage';
import { getAllPosts, getAlbumBySlug, getAlbums } from '../lib/api';
import { getPostBySlug } from '../lib/markdownAccess';
import PostPage from '/components/PostPage';
import markdownToHtml from '../lib/markdownToHtml';
// The Main Component
export default function SlugPage({ post, album }) {
if (post) return <PostPage post={post} />;
if (album) return <AlbumPage album={album} />;
}
// Get static props - gather required page data based on page
export async function getStaticProps({ params }) {
// . . .
const post = getPostBySlug(params.slug, [...);
if (post) {
return {
props: {
post,
},
};
}
return {
notFound: true,
};
}
// Get the static paths for all posts and pages
export async function getStaticPaths() {
const posts = getAllPosts(['slug']);
const albums = getAlbums();
const slugs = [...albums, ...posts].map((contentObj) => contentObj.slug);
return {
paths: slugs.map((slug) => {
return {
params: {
slug,
},
};
}),
fallback: 'blocking',
};
}
The post
object contains the raw markdown and meta data for the page. All of the site's pages are built from that markdown and are rendered to JSX through this component:
import markdownStyles from './markdown-styles.module.css';
import Markdown from 'markdown-to-jsx';
import Link from 'next/link';
import Image from 'next/image';
import NextLink from './NextLink';
export default function PostBody({ content }) {
return (
<div className="markdown">
<Markdown
options={{
overrides: {
a: NextLink,
img: BlogImage,
},
}}
>
{content}
</Markdown>
</div>
);
}
// const BlogImage = (props) => <Image {...props} width={800} layout="fill" />;
const BlogImage = (props) => (
<a href={props.src} target="_blank" rel="noopener noreferrer">
<img {...props} />
</a>
);
Markdown to JSX is doing the heavy lifting of rendering my markdown annotations to html. I've also plugged in a few custom overrides to make use of Next features, such as the NextLink
to handle routing through the app, as well as an img
override to open in a new tab by default.
Adding In Highlight.js
Highlight.js is a flexible library that can do exactly what I'm looking for, both on the client and server.
Since I'm building static pages, I'll reach for their server implementation to call:
html = hljs.highlightAuto('<h1>Hello World!</h1>').value
I could use their client side approach, wrapped up in a useEffect
. However, that adds to the js bundle sent down the wire. Not to mention, I'd get this ugly flicker effect once the styles kicked in.
So, I'm opting to build another override.
Markdown renders code in a <pre>
and nested <code>
tag. So I'll add my own components to plugin the synchronous syntax highlighting:
First, importing highlight.js and adding my override:
import hljs from 'highlight.js';
export default function PostBody({ content }) {
return (
<div className="markdown">
<Markdown
options={{
overrides: {
a: NextLink,
img: BlogImage,
pre: Pre,
},
}}
>
{content}
</Markdown>
</div>
);
}
And then writing my custom components:
const CodeBlock = ({className, children}) => {
children = hljs.highlightAuto(children, ['java', 'javascript', 'python', 'react', 'yaml']).value;
return (
<pre>
<code dangerouslySetInnerHTML={{__html: children}} />
</pre>
);
}
const Pre = ({children, ...rest}) => {
return <pre {...rest}>{children}</pre>;
}
ViolΓ ! The colors you see above are thanks to these changes!
New Album β Dog Angst πΆ
Lucy's been listening to my teenage emo CDs! Now she's all moody.
Purchase on π€ Bandcamp and Listen on π Spotify or any of your favorite streaming services!