

Table of Contents
In this post, I’m sharing how I built a secure and automated CI/CD stack for one of my Java projects. The goal was to streamline the entire process—from code commit to deployment—while ensuring code quality, security, and traceability. I used tools like Jenkins, Maven, Checkmarx, and JFrog Artifactory, along with Python and Jinja2 for dynamic configuration. If you’re looking to build a reliable pipeline for your Java apps, this walkthrough will give you a solid foundation.
Why this matters: In modern development workflows, especially with Java ecosystems, having a CI/CD pipeline that addresses security, automation, and traceability isn’t optional—it’s essential. This practical setup showcases how our team aligns these aspects cohesively.
Why this matters:
In modern development workflows, especially with Java ecosystems, having a CI/CD pipeline that addresses security, automation, and traceability isn’t optional—it’s essential. Particularly for Java applications, which often support critical business operations, the pipeline must ensure that every change is verifiable, secure, and deployable across multiple environments without disruption.
This practical setup showcases how our team combines open-source tools and proven practices to address common challenges around integration, security scanning, artifact management, and configuration flexibility. For teams aiming to scale or operate in compliance-driven sectors, adopting such structured pipelines is not just beneficial but necessary.
Tools & Requirements
Component | Role |
Git | Source code versioning |
Maven | Java build tool |
Jenkins | CI/CD automation |
Checkmarx | Static application security testing |
JFrog Artifactory | Artifact repository |
Python & Jinja2 | Dynamic config rendering |
Target server (VM/Docker) | Deployment destination |
Project Structure
Assume your Java project has this structure:
my-java-app/
├── pom.xml
├── src/
├── Jenkinsfile
├── config-template.yml.j2
├── app_values.yml
└── generate_config.py
The pom.xml builds a .tar.gz under target/. The generate_config.py renders config.yml for deployment.
Git and Webhooks
Configure your Git repository (GitHub, GitLab, Bitbucket, etc.) to trigger Jenkins using a webhook on every push to your main or dev branch.
Setting Up Jenkins
Required Plugins:
- Maven Integration
- Git
- Checkmarx plugin (or CLI support)
- Artifactory plugin
- Credentials plugin
Add Credentials in Jenkins:
- JFrog API Key(ID: jfrog-api-key)
- Target Server SSH Key(optional, if deploying to VM)
Jenkinsfile (Pipeline as Code)
pipeline {
agent any
environment {
ARTIFACT_NAME = “my-java-app.tar.gz”
ARTIFACTORY_URL = “https://your.jfrog.io/artifactory”
REPO_NAME = “generic-local”
}
stages {
stage(‘Checkout’) {
steps {
git ‘https://your-repo-url’
}
}
stage(‘Build’) {
steps {
sh ‘mvn clean package’
}
}
stage(‘Test’) {
steps {
sh ‘mvn test’
}
}
stage(‘Security Scan – Checkmarx’) {
steps {
sh ‘cx scan –project-name “my-java-app” –location-type folder –location-path .’
}
}
stage(‘Upload to Artifactory’) {
steps {
withCredentials([string(credentialsId: ‘jfrog-api-key’, variable: ‘JFROG_API_KEY’)]) {
sh ”’
jfrog config add artifactory-server \
–url=$ARTIFACTORY_URL \
–user=your-jfrog-username \
–apikey=$JFROG_API_KEY \
–interactive=false
jfrog rt u “target/${ARTIFACT_NAME}” “${REPO_NAME}/my-java-app/${ARTIFACT_NAME}”
”’
}
}
}
stage(‘Generate App Config’) {
steps {
sh ”’
pip install –user PyYAML Jinja2
python3 generate_config.py
”’
}
}
stage(‘Deploy to Server’) {
when {
branch ‘main’
}
steps {
withCredentials([string(credentialsId: ‘jfrog-api-key’, variable: ‘JFROG_API_KEY’)]) {
sh ”’
jfrog rt dl “${REPO_NAME}/my-java-app/${ARTIFACT_NAME}” –flat
scp ${ARTIFACT_NAME} user@target-server:/opt/myapp/
scp config.yml user@target-server:/opt/myapp/config/
ssh user@target-server ”
cd /opt/myapp &&
tar -xzf ${ARTIFACT_NAME} &&
./run.sh
”
”’
}
}
}
}
post {
always {
junit ‘target/surefire-reports/*.xml’
}
}
}

Python Script for Config Rendering
generate_config.py:
import yaml
from jinja2 import Environment, FileSystemLoader
# Load values
with open(‘app_values.yml’) as f:
values = yaml.safe_load(f)
# Setup Jinja2
env = Environment(
loader=FileSystemLoader(‘.’),
trim_blocks=True,
lstrip_blocks=True
)
template = env.get_template(‘config-template.yml.j2’)
# Render template with values
output = template.render(values)
# Save to final config.yml
with open(‘config.yml’, ‘w’) as f:
f.write(output)

What This Pipeline Does
- Checks out the code
- Builds the Java project using Maven
- Runs unit tests
- Scans code for vulnerabilities using Checkmarx
- Uploads .tar.gz artifact to JFrog Artifactory
- Generates config.yml from template and values using Python & Jinja2
- Deploys the artifact and config to a target server
Best Practices
- Use different branches for dev, QA, and prod pipelines.
- Store sensitive data using Jenkins credentials.
- Use artifact versioning (my-java-app-1.0.0.tar.gz) for traceability.
- Monitor pipeline with Jenkins Blue Ocean or email notifications.
- Add Slack/MS Teams integrations for alerts.
Conclusion
Setting up this CI/CD pipeline has been valuable for managing and deploying my Java applications. With everything integrated—Git, Maven, Jenkins, Checkmarx, Artifactory, and Python-based configuration—I now have a fully automated and secure workflow that reduces manual effort and operational risk.
I’m able to:
- Validate code quality and security early
- Package and version artifacts reliably
- Generate environment-specific configs dynamically
- Push builds to any server with a single Git push
This setup has improved traceability, security posture, and consistency in production deployments.
Next Steps: For teams looking to extend these capabilities, integrating Docker, Kubernetes, and GitOps patterns can further enhance deployment agility and scalability across cloud-native environments.
If you’re building a similar pipeline or want to adapt it for Docker, Kubernetes, or cloud-native environments, I’d be interested to hear your approach or share insights where relevant.