The Modular Layer JasperReports’ Powerful Engine Was Missing. Part 1: Where Jasper Came From and Why It Lacked Modularity A developer released version 3.0.0 of jasper-modular, an open-source library that adds a modular layer to the JasperReports reporting engine. The project, built over roughly half a year of work since its first release, aims to address JasperReports' lack of modularity, a limitation rooted in the engine's origins as a self-contained, page-oriented document tool created by Teodor Danciu in the early 2000s. The writeup traces JasperReports' history and design philosophy ahead of a second part covering practical usage. Researching such big and complex products as JasperReports, and even more building your own practical tool for it, is clearly not a trend of 2026. This is the year when likes in media and stars on GitHub go to the next AI repository, written by another AI for a third AI. So the text and the work itself may look out of place. It is my personal project. I don't claim it is something revolutionary, but it surely has every right to take a couple of lines in the dependencies of very different projects. The library is an attempt to generalize my experience deeply, and a personal challenge that is valuable for every developer: not just to write code, but to create something the community will actually use. And a line in the CV, of course. Part of the thought from the first paragraph is relevant here too. By different estimates, a noticeable share of the traffic to developer sites and media already comes from AI agents, so this text is for them as well. These guys now also add those same lines to other people's projects. Now I will talk about version 3.0.0 of the library, jasper-modular. Half a year of work since its first release removed various rough edges and fixed some weak spots that were hard to notice. But first — a bit of background. I worked with JasperReports on several projects, and the concept I am going to describe was born in pain, from many mistakes collected over the years. But at work there was no time to build a separate reporting tool, so I had to do it in my free time. The text has two parts. This one is not a manual. It is about where Jasper came from, why it is designed the way it is, what happens with it now and which ideas my approach is built on. The second part is practice: how to add the library to a project and work with it, with the code and the templates. At the very beginning of the 2000s the Romanian developer Teodor Danciu was evaluating a big Java project where a lot of complex documents had to be generated and printed. He found that the ready-made solutions were too expensive for the project. He had experience with Windows report generators like Crystal Reports, and he decided that making a similar tool in Java would not be so hard. These are his own words http://web.archive.org/web/20050223161242/http://rootprompt.org/article.php3?article=8332 from an interview in 2005. At that time he also described http://web.archive.org/web/20020118143142/http://jasperreports.sourceforge.net/ the goal of the project. The engine can be embedded into any Java application, and its main purpose is page-oriented documents, ready to print. So from the very beginning it was a tool for documents, and how it would fit into the layers of an application was not part of the task. In the world of Crystal Reports and its relatives, a report is a self-sufficient design file that owns everything: the query to the database, the bands, grouping, totals, formatting. The original tutorial from 2002 http://web.archive.org/web/20020215144909/http://jasperreports.sourceforge.net/tutorial/index.html says directly that the whole structure of a report is a stack of sections from the title to the summary. And there it also says that the SQL query can be written right inside the report definition. A report was an application in miniature, a thing in itself, fully self-contained. By default it connected to the database by itself and ran its own query, no matter who made it, a developer or an analyst. In the client-server nineties this was normal, because applications went to the database directly anyway, and the business logic lived either in the client itself or in stored procedures. Self-sufficiency has a flip side. If a report lives on its own, it must be able to take data from anywhere, and over time many ways to do it appeared. There were several of them from the very beginning. The same 2002 tutorial, next to the SQL query inside the template, describes passing a ready ResultSet from code. Later the number of ways only grew: queries over XML since 2004 and JSON since 2011 , data adapters, when the report itself knows the database address and the password, passing the parent data source to a subreport, manual passing of parameters down the chain of subreports, and everything else Jasper collected in twenty-five years. None of them is the main one, and every project chooses its own. Even if you don't keep the query in the template, there are no fewer options: build SQL in Java and put it into the template query through a parameter, execute it yourself and pass the result, get the data via JPA and wrap it into beans. Reporting turns into a forest of unknown solutions. In every new project reports are built in their own way, and you have to figure everything out again. The most painful place in this zoo is subreports. In Jasper they are the way to build a document from blocks. The grid of sections itself is flat, and you can insert a separately designed block into it only with a subreport. And here the hell begins: complicated passing of parameters, their manual binding to types and manual injecting too, design, parsing data inside, plus the complicated mechanism of "connecting" a subreport itself. On a real example it looks like this. Take an invoice with a block of items. To make one new field appear in this block, when data comes from Java you have to write it in three places: in the Java code where the parameter map is built, in the root template where it is passed to the subreport, and once again in the subreport itself. All this binding parameter declarations in templates, passing them down the chain and the map of values in Java I will call wiring from now on. And in all three places the field name is just a string that nobody checks. Make a mistake in one letter — and in the PDF you get "null" or an empty cell instead of the value, and not a single error in the logs. With SQL in the template it is not easier. You need to add the new column to the query, declare a field in the template with exactly the same name and not make a mistake with the type. You will find out about a typo only when somebody runs the report. Here it is important to clarify my position. I don't criticize Jasper. It is not a "badly designed library for developers" that I try to fix. It is a well-designed heir of a completely different tradition. A great engine and technology, to which I add what seems useful for the convenience of developers. And in Jasper itself people saw the same problem and tried to answer it. Since version 6 it has report books, where instead of bands a report contains parts. Each part is a separate report with its own pages and even its own page size, parts can be nested into each other, and you can build a table of contents for the document. It is an attempt to build a document as a tree. Books still work today and could have become a good answer. But it is a tree of documents, not of blocks inside a page. You cannot build the header, a table and the footer of one page from parts. Books were designed from the start as assembling a document from whole pages, with a table of contents and different page formats. And there is no contract between code and template there either. Parts are connected by file paths, and data goes into each of them by hand, by string names. In the official sample, the database connection and the needed parameters are passed to the parts with data one by one, so it is the same wiring, only one level higher. Both the zoo and the manual wiring grow from the design of Jasper itself. In application development, such confusion, where getting data, passing it and displaying it are mixed together, has been solved for a long time by separating layers: the model separately, the view separately, and the controller between them. This is MVC, and the problem is not that nobody had thought of it back then. By 2001 the pattern itself was more than twenty years old, and in the same year as Jasper, version 1.0 of Struts came out, a framework for Java web applications built exactly on MVC. The MVC roles also exist in Jasper from the first day: the template draws, the data source carries the data, the engine fills. And, as we saw, it could take data from outside from the very beginning too. Three things are missing: good mapping between Java objects and the template the contract between the model and the template is string-based, manual and checked by nobody , limits on the choice of approaches how to inject, how to pass data, how to parse data and so on and boundaries between layers, so the view is allowed to go to the database directly. A parameter in Java and a parameter in XML match only because somebody typed them the same way by hand. The pattern was sketched, but its separation never happened, because nobody wanted to separate the layers strictly back then. Report authors of that time saw the right of a template to go to the database by itself not as a hole in the boundary but as the main convenience. And there was nothing in the language itself to check such separation at compile time. Generics, annotations and annotation processing appeared in Java only in 2004–2006, and exactly on them the library is built now. Later the business logic moved into the server layers of applications, but reporting did not move with it and stayed attached to the database. Maybe something changed over time? Twenty-five years have passed. What is happening with Jasper now — in short. The engine is alive and supported. Version 7.0.8 came out in August 2026, and vulnerabilities get fixed. Since version 7, the modules for web and JPA moved from javax to jakarta: Servlet 6.0 instead of 4.0, Persistence 3.1 instead of 2.2, and for old applications the javax variants are kept. But the design of the core has basically not changed since then. Danciu compares https://github.com/Jaspersoft/jasperreports/issues/116 issuecomment-569786343 template compatibility with Word documents. A JRXML created ten years ago must open in the new engine the same way as a DOC from 2003 opens in a new Office. So for them a template is a document format, not code with an API, and the main task is that templates with many years of history continue to work. The layer of integration with applications belongs to nobody. Spring removed Jasper support already in version 5, in 2017. And when in 2026 users asked about Spring Boot 4 support, Danciu answered https://github.com/Jaspersoft/jasperreports/issues/582 issuecomment-4370496765 that he still had not heard what exactly the problem was, then showed with a sample that the engine works under Spring Boot 4, and closed the ticket. For the engine team, embedding into applications is somebody else's layer, and by their logic this is right. But nobody else picked it up either. For the query "jasperreports spring boot" you find third-party blog articles, mostly from 2019–2023, and they teach you to build a report by hand: compile, fill, export step by step. And one of them still shows JasperReportsPdfView , the same class that Spring removed in 2017. The core compilation, filling, pixel-perfect layout, export has worked reliably for a quarter of a century, and you can build on it without worry. But the fact remains that developers inherited a tool whose center of gravity is in another world. Nobody on that side is going to move it, so we have to do it ourselves. And they expect two things they got used to during the last ten years or so. The first is that layers are separated: data separately, view separately, MVC in one form or another. The second is that the interface is built as a tree of reusable blocks: the DOM tree in HTML, components in the frontend, their analogues in mobile development. You write a block once, and insert it wherever you need. Jasper has neither of these in a convenient form, and it makes no sense to wait for it from its team, because they make an engine for documents, not a layer for applications. This gap between what a developer is used to and what the engine gives is exactly what the library closes. First with built-in limits that reduce the zoo of options to one, and then with three ideas on top of them. So here is the idea. What stays as it was: What is added the things the core does not have : The three ideas from the list the tree, the layers and the object stand on this standard, and none of them is new by itself. The point is that they work only together. A strict contract without composition gives the same monolithic template, only checked, and composition without a contract is the current subreport hell. And all three come from one move, turning a subreport into a field of a Java class. The first thing the library does is consciously ignore all the other ways Jasper lets you get and pass data, that whole zoo listed above. Only what fits the modular MVC approach stays. Data comes as objects, and wiring is generated. This does not touch layout, and everything Jasper can do in design stays yours. You can still do anything the old ways, past the library, but it will intentionally not help you with that. In exchange it gives one standard, and in new reports there is no reason to go around it anymore. In classic Jasper, the structure of a report is defined by the engine. Remember the 2002 tutorial. The whole structure is built on sections: title, pageHeader, columnHeader, groupHeader, detail, groupFooter, columnFooter, pageFooter, summary. So the "structure" is a set of ready cells. They are fixed, they cannot be nested into each other, the engine defines their set, and the developer puts the content into the grid they were given. My approach changes who owns the structure. The library goes around this predefined structure and simplifies it, to make an analogue of the DOM tree that is flexible to build and reuse. The root report is an empty container. De facto the library builds the report in one