Skip to content

Commit a52daba

Browse files
feat(spring-jasper-example): implement client report generation with JasperReports
1 parent c2bb755 commit a52daba

File tree

14 files changed

+273
-18
lines changed

14 files changed

+273
-18
lines changed

spring-jasper-example/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,6 @@ COPY --from=extract build/target/extracted/spring-boot-loader/ ./
4242
COPY --from=extract build/target/extracted/snapshot-dependencies/ ./
4343
COPY --from=extract build/target/extracted/application/ ./
4444

45-
EXPOSE 80
45+
EXPOSE 8082
4646

4747
ENTRYPOINT [ "java", "org.springframework.boot.loader.launch.JarLauncher" ]

spring-jasper-example/README.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,24 @@
11
# spring-jasper-example
2+
3+
---
4+
5+
## Overview
6+
7+
Basic Java Spring (Java 21) project demonstrating report generation with JasperReports.
8+
Includes loading `.jasper` templates, populating them with mock data, and exporting to PDF via a REST endpoint.
9+
10+
---
11+
12+
## Tech stack
13+
14+
- Spring Boot
15+
- Java 21
16+
- JasperReports
17+
18+
---
19+
20+
## Profiles you can activate
21+
22+
- **client-list-dev** – Used in Docker, in the future, it will use pre-compiled `.jasper` files instead of `.jrxml`. Activates the internal module that generates a simple client report.
23+
- **client-list-local** – Used locally, activates the internal module that generates a simple client report and use `.jrxml`.
24+

spring-jasper-example/compose.yaml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ services:
55
build:
66
context: .
77
ports:
8-
- "80:80"
8+
- "8082:8082"
99
environment:
10-
SPRING_PROFILES_ACTIVE: prd
10+
SPRING_PROFILES_ACTIVE: client-list-dev
11+
SERVER_PORT: 8082

spring-jasper-example/pom.xml

Lines changed: 57 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@
2121
<java.version>21</java.version>
2222
<spring.boot.version>3.5.4</spring.boot.version>
2323
<jasper.version>7.0.3</jasper.version>
24-
</properties>
24+
<lombok.version>1.18.38</lombok.version>
25+
</properties>
2526

2627
<dependencies>
2728

@@ -35,8 +36,33 @@
3536
<groupId>net.sf.jasperreports</groupId>
3637
<artifactId>jasperreports</artifactId>
3738
<version>${jasper.version}</version>
39+
<exclusions>
40+
<exclusion>
41+
<groupId>commons-logging</groupId>
42+
<artifactId>commons-logging</artifactId>
43+
</exclusion>
44+
</exclusions>
3845
</dependency>
3946

47+
<dependency>
48+
<groupId>net.sf.jasperreports</groupId>
49+
<artifactId>jasperreports-pdf</artifactId>
50+
<version>${jasper.version}</version>
51+
<exclusions>
52+
<exclusion>
53+
<groupId>commons-logging</groupId>
54+
<artifactId>commons-logging</artifactId>
55+
</exclusion>
56+
</exclusions>
57+
</dependency>
58+
59+
<dependency>
60+
<groupId>org.projectlombok</groupId>
61+
<artifactId>lombok</artifactId>
62+
<optional>true</optional>
63+
<version>${lombok.version}</version>
64+
</dependency>
65+
4066
<dependency>
4167
<groupId>org.springframework.boot</groupId>
4268
<artifactId>spring-boot-starter-test</artifactId>
@@ -46,18 +72,39 @@
4672

4773
</dependencies>
4874

49-
<build>
75+
<build>
76+
77+
<plugins>
5078

51-
<plugins>
79+
<plugin>
80+
<groupId>org.apache.maven.plugins</groupId>
81+
<artifactId>maven-compiler-plugin</artifactId>
82+
<configuration>
83+
<annotationProcessorPaths>
84+
<path>
85+
<groupId>org.projectlombok</groupId>
86+
<artifactId>lombok</artifactId>
87+
</path>
88+
</annotationProcessorPaths>
89+
</configuration>
90+
</plugin>
5291

53-
<plugin>
54-
<groupId>org.springframework.boot</groupId>
55-
<artifactId>spring-boot-maven-plugin</artifactId>
56-
<version>${spring.boot.version}</version>
57-
</plugin>
92+
<plugin>
93+
<groupId>org.springframework.boot</groupId>
94+
<artifactId>spring-boot-maven-plugin</artifactId>
95+
<version>${spring.boot.version}</version>
96+
<configuration>
97+
<excludes>
98+
<exclude>
99+
<groupId>org.projectlombok</groupId>
100+
<artifactId>lombok</artifactId>
101+
</exclude>
102+
</excludes>
103+
</configuration>
104+
</plugin>
58105

59-
</plugins>
106+
</plugins>
60107

61-
</build>
108+
</build>
62109

63110
</project>
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package com.io.example.clientList.controller;
2+
3+
import com.io.example.clientList.service.ReportService;
4+
import lombok.RequiredArgsConstructor;
5+
import org.springframework.context.annotation.Profile;
6+
import org.springframework.http.HttpHeaders;
7+
import org.springframework.http.MediaType;
8+
import org.springframework.http.ResponseEntity;
9+
import org.springframework.web.bind.annotation.GetMapping;
10+
import org.springframework.web.bind.annotation.RestController;
11+
12+
@RestController
13+
@Profile({"client-list-dev", "client-list-local"})
14+
@RequiredArgsConstructor
15+
public class ReportController {
16+
17+
private final ReportService reportService;
18+
19+
@GetMapping("/reports/clients")
20+
public ResponseEntity<byte[]> getClientsReport() {
21+
byte[] pdf = this.reportService.generateClientReport();
22+
return ResponseEntity.ok()
23+
.header(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=clients.pdf")
24+
.contentType(MediaType.APPLICATION_PDF)
25+
.body(pdf);
26+
}
27+
28+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.io.example.clientList.dto;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.Data;
5+
import lombok.NoArgsConstructor;
6+
7+
@Data
8+
@NoArgsConstructor
9+
@AllArgsConstructor
10+
public class ClientDto {
11+
12+
private Integer id;
13+
private String name;
14+
private String country;
15+
16+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package com.io.example.clientList.service;
2+
3+
import com.io.example.clientList.dto.ClientDto;
4+
import net.sf.jasperreports.engine.*;
5+
import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;
6+
import org.springframework.context.annotation.Profile;
7+
import org.springframework.core.io.ClassPathResource;
8+
import org.springframework.stereotype.Service;
9+
10+
import java.io.InputStream;
11+
import java.util.*;
12+
13+
@Service
14+
@Profile({"client-list-dev"})
15+
public class ReportDevService implements ReportService {
16+
17+
private static final List<ClientDto> clients = Arrays.asList(
18+
new ClientDto(1, "Alice", "Brazil"),
19+
new ClientDto(2, "Bob", "USA"),
20+
new ClientDto(3, "Carlos", "Spain")
21+
);
22+
23+
@Override
24+
public byte[] generateClientReport() {
25+
return this.exportReportToPdf();
26+
}
27+
28+
/*
29+
* TODO: In the future, this should be migrated to use:
30+
*
31+
* InputStream stream = new ClassPathResource("reports/client-list.jasper").getInputStream();
32+
* JasperReport jasperReport = (JasperReport) JRLoader.loadObject(stream);
33+
*
34+
* This approach is the recommended way in production environments.
35+
*/
36+
37+
private byte[] exportReportToPdf(){
38+
try {
39+
InputStream stream = new ClassPathResource("reports/client-list.jrxml").getInputStream();
40+
JasperReport jasperReport = JasperCompileManager.compileReport(stream);
41+
JRBeanCollectionDataSource dataSource = new JRBeanCollectionDataSource(clients);
42+
JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, new HashMap<>(), dataSource);
43+
return JasperExportManager.exportReportToPdf(jasperPrint);
44+
} catch (Exception e) {
45+
throw new RuntimeException(e);
46+
}
47+
}
48+
49+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package com.io.example.clientList.service;
2+
3+
import com.io.example.clientList.dto.ClientDto;
4+
import net.sf.jasperreports.engine.*;
5+
import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;
6+
import org.springframework.context.annotation.Profile;
7+
import org.springframework.core.io.ClassPathResource;
8+
import org.springframework.stereotype.Service;
9+
import java.io.InputStream;
10+
import java.util.*;
11+
12+
@Service
13+
@Profile({"client-list-local"})
14+
public class ReportLocalService implements ReportService {
15+
16+
private static final List<ClientDto> clients = Arrays.asList(
17+
new ClientDto(1, "Alice", "Brazil"),
18+
new ClientDto(2, "Bob", "USA"),
19+
new ClientDto(3, "Carlos", "Spain")
20+
);
21+
22+
@Override
23+
public byte[] generateClientReport() {
24+
return this.exportReportToPdf();
25+
}
26+
27+
private byte[] exportReportToPdf(){
28+
try {
29+
InputStream stream = new ClassPathResource("reports/client-list.jrxml").getInputStream();
30+
JasperReport jasperReport = JasperCompileManager.compileReport(stream);
31+
JRBeanCollectionDataSource dataSource = new JRBeanCollectionDataSource(clients);
32+
JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, new HashMap<>(), dataSource);
33+
return JasperExportManager.exportReportToPdf(jasperPrint);
34+
} catch (Exception e) {
35+
throw new RuntimeException(e);
36+
}
37+
}
38+
39+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.io.example.clientList.service;
2+
3+
public interface ReportService {
4+
byte[] generateClientReport();
5+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
server:
2+
port: ${SERVER_PORT:8082}
3+
4+
spring:
5+
6+
application:
7+
name: spring-jasper-example-client-list-dev

0 commit comments

Comments
 (0)