Smalltalk before Longtalk ;)
(Of course I don't want to encourage you to read everything of this long detailed post! The bold markers may already be enough to solve your problems but I found it worth documenting this tricky stuff in some more detail!)
Since I invested another couple of hours on this (after I resolved it some weeks ago, had a change now, but forgot to document it properly, forgot how I did it and could not retrieve this info again in any form - when uploading and configuring to/in JasperServer) ... here is some aggregated functionality mentioned on various sites regarding subreport referencing, how it works and what one can try ...
(I'll update mine or other findings in here if there hopefully will be some)
Short cut details / best practices?!4
Till maybe Jasper functionality provides a similar "wrapping" solution itself ...
To workaround all the problems related to running the *.jrxml, *.jasper files either locally in Preview mode or remotely on a JasperServer I am now using the following approach which allows to work with only a single *.jrxml file, that will work locally and remotely without modifications, in a multi-developer environment, supporting independent refactoring of dir structures (paths, names) per environment (= as it should ;-) ):
- using some jasper-utils-*.jar
- put it in your project (Java) class path (
Project->Properties->Java Build Path->Libraries->Add
)
- put it in your
../jasperserver/WEB-INF/lib/
folder
- referencing some custom Jasper Java Scriptlet
jr.utl.EnvScriptlet
that does the ugly subreport path/reference magic in your master reports
- define the
REPORT_SCRIPTLET
by adding an attribute to your master report: report properties -> Report -> Data Set -> Scriptlet Class: jr.utl.EnvScriptlet
using some custom properties file jr.utl.properties
or otherwise supplied system properties (any other way to set the Java system properties would be fine as well and work - where already set up properties will override loaded file properties) to configure the different environments including your
- current environment information via
jr.utl.env
property (prod, myOsUsrName, test, demo, staging, local, ...)
- which determines how the subreport references must be generated / look like
- server subreport parent directory property references
- take e.g. these property file contents and put one per environment here:
on your servers: ../jasperserver/WEB-INF/classes/jr.utl.properties
jr.utl.env=prod
mycompany.local.jr.gui.rep.subrep1.parentdir=repo:/x/y/z/
mycompany.local.jr.gui.rep.subrep2.parentdir=repo:/x/y/z/
mycompany.local.jr.gui.rep.subrep3.parentdir=repo:/x/y/foobar/
in your local JasperSoft Studio (Eclipse) Java src/build path: e.g. ../myrepproject/src/java/jr.utl.properties
jr.utl.env=dietrian
mycompany.local.jr.gui.rep.subrep1.parentdir=D:/reporting/src/reports/
mycompany.local.jr.gui.rep.subrep2.parentdir=D:/reporting/src/reports/
mycompany.local.jr.gui.rep.subrep3.parentdir=D:/reporting/src/reports.otherdir/
to achive source modification independency in our environments we parameterized those values and generate them once via some workspace-dependent/user-specific local.properties
file, based on this idea:
|- build.xml (containing the ANT build magic)
|- build.properties (containing global properties)
|- local.properties (ignored in version control, e.g. .hgignore, user-specific generated from local.template.properties)
|- local.template.properties (source for ANT build task generating the local.properties above)
|- mycomp.local.proj.reporting.dir=D:/reporting
|- src/reports
|- jr.utl.properties (ignored in version control, user-specificly generated based on template below)
|- jr.utl.template.properties (source for ANT build task generating the jr.utl.properties above)
jr.utl.env=${user.name}
mycompany.local.jr.gui.rep.subrep1.parentdir=${mycomp.local.proj.reporting.dir}/src/reports/
mycompany.local.jr.gui.rep.subrep2.parentdir=${mycompany.local.jr.gui.rep.subrep1.parentdir}
mycompany.local.jr.gui.rep.subrep3.parentdir=${mycomp.local.proj.reporting.dir}/src/reports.otherdir/
defining your BASE_DIR
master report parameters as e.g.
$P{REPORT_SCRIPTLET}.getProp("mycompany.allsubreports.parentdir")
(matching some environment-dependent property in your jr.utl.properties
file)
- defining the master subreport expressions as e.g.
jr.utl.EnvScriptlet.getSubrepPath( $P{BASE_DIR}, "subrep1.jrxml")
- automatically resolving the values from properties you could also use e.g. these variants:
jr.utl.EnvScriptlet.getSubrepPathByPropKey( $P{BASE_DIR}, "mycompany.local.jr.gui.rep.subrep1.name")
jr.utl.EnvScriptlet.getSubrepPathByPropKeys( "mycompany.local.jr.gui.rep.subrep1.parentdir", "mycompany.local.jr.gui.rep.subrep1.name")
$P{REPORT_SCRIPTLET}.getSubrepPath(...)
does not work here :-( (I don't know why)
- do not forget to restart your server when you put all the files on the server!
(4: Of course I am still seeing some minor improvements here, but it seems much better than all the ugly solutions I found till now. Improvements I would see:
- using the
REPORT_SCRIPTLET
or scriptlet functionality may not be the best way to go, but it will probably work in the vast majority of use cases
- although both existing Jasper classes suggest this they do not seem to be able to handle the above properly:
(5: the relevant special handling is encoded here: EnvScriptlet.java/getSubrepPath(String,String,boolean,String[]))
Intro (Background)
First thing to know is that the handling/setup in JasperStudio is quite different from the handling on Jasper Server (Repository)5 ...
suppose we have the following enviroments:
- our Eclipse install dir:
C:\eclipse\
- our Eclipse (Report) workspace:
C:\workspace\
- our report project under:
C:\workspace\report-project\
- our reports under:
C:\workspace\report-project\src/reports
- a master report
C:\workspace\report-project\src/reports/masterrep.jrxml
- some subreport
C:\workspace\report-project\src/reports/subrep1.jrxml
- another subreport
C:\workspace\report-project\src/reports/somesubdir/subrep2.jrxml
- the
BASE_DIR
(explained in next section) in our workspace master report is set to C:\workspace\report-project\src/reports/
- our Jasper Report Server GUI repo id-path of our master report will be:
/x/y/z/
(which is not to-be-confused with the visual named-path, e.g. which could be Financial Reports/Expenses/Current Year
)
In general: Jasper Studio, JasperServer
(and other "Jasper runtime environments" like custom Java Jasper package usage):
- it seems a good practice to declare a report parameter "prefix" which can vary depending on your Jasper runtime environment e.g. named
BASE_DIR
- important here is that it seems best to assume the suffixed
/
may be included1 because there are cases where you may have/want to use it in a way where it should be an empty or "unslashed" path expression
- e.g.
$P{BASE_DIR} + "subrep1.jrxml"
which should resolve to
repo:subrep1.jrxml
- see e.g. here for more details (look for
SUBREPORT_DIR
)
(1: which I personally find a bad practice in general (not looking at Jasper Reports in this respect) when dealing with directory-like structures)
JasperStudio Designer (Eclipse Plugin)
(the official IReport successor with loads of more functionality)
(if you do not use the preview functionality this may be uninteresting to you)
- unfortunately I found no practical way to fully support (normal) "team-development" with subreports (and likely other relative resources as well), meaning here the (currently to me unknown) inexistent possiblity to separate local paths and *.jrxml files :-(
- e.g. if you have a version control system in place and work in different environments (different local paths to repos and/or different developers) the master report has to contain a local path to your subreport in some way)
- I tried different approaches that failed:
- relative path expressions in
BASE_DIR
do not work since the working directory is the eclipse dir, e.g. C:\eclipse
Eclipse->Window->Preferences->JasperStudio->Properties->Add
e.g. my.base.dir
- it is not available in the Preview mode, e.g. via
new java.io.File(System.getProperty("my.base.dir")).getCanonicalPath() + "/"
for our BASE_DIR
expression (these props may be only used by the designer itself, but not set in preview runs)
- just in case you may stumble upon (as I did):
Eclipse->Window->Preferences->JasperStudio->Report Execution->Virtualizer Temporary Path
is something unrelated (not useful here) dealing with the storage of the report result "caching"
- of course I could write an ANT task to replace these local pattern based on a regexp filter copy on every usage/checkout, but that seems not a good way to handle this
- if you solely want to work with
*.jrxml
files (as I do3) you have to reference some subrep1.jrxml
like this: net.sf.jasperreports.engine.JasperCompileManager.compileReport($P{BASE_DIR} + "subrep1.jrxml")
(3: I don't need the *.jasper
files explicitely and do not see why I want to deal with them. BTW the JasperServer WebGUI only seems to support the upload of *.jrxml
files)
JasperServer Web GUI
(e.g. provided by some Tomcat application server and storing its data in some postgres database)
Scenario 1: reference attached subreport resource(s)
- if you do not want to reuse your report in general, it seems fine to add your supreport to your master report (so it is not visible in the GUI repo tree - see below subitem how you could reference it outside of your master anyways)
- if you attach your subreport it should in general have its file name as its resource id, e.g. our
subrep1.jrxml
from above is uploaded with a resource id of subrep1.jrxml
(thus making the handling of local design references and server references less complicated)
- taking the example reports from above we have to set our
BASE_DIR
to repo:
in the to-be-uploaded master report
- thus the subreport expressions
$P{BASE_DIR} + "subrep1.jrxml"
and $P{BASE_DIR} + "somesubdir/subrep2.jrxml"
should work on the server as well
- NOT recommended!: you could still reference these reports from other reports with absolute paths like this2:
repo:/x/y/z/masterrep.jrxml_files/masterrep.jrxml_
(2: which I would not recommend in this case; it's undocumented and may change; better put your subreports then into the "GUI repo path" as described below)
Scenario 2: reference repo subreport resource(s)
Additional hints
Since I like to automate the report design and deployment process as much as it makes sense I wrote some ANT tasks that handle the local *.jrxml file to deployable *.jrxml file transformations regarding the BASE_DIR
and the other transformations.
SQL helpful to easily investigate the resource id path structures in a jasper server postgres meta database (following something like jdbc:postgresql://myjasperhost/jasperserver
connecting e.g. with the postgres user):
select
f.id as folder_id,
r.id as res_id,
case when f.hidden = true then 1 else 0 end as hidden,
f.uri||case when f.uri = '/' then '' else '/' end||coalesce(r.name,'') as res_uri,
r.resourcetype,
r.creation_date,
r.update_date,
f.uri,
r.name,
-- less important
r.version,
r.parent_folder,
r.childrenfolder,
f.parent_folder,
f.version,
f.name
-- select *
from jiresourcefolder f
left outer join jiresource r on (r.parent_folder = f.id)
where not f.uri like '/themes%'
order by f.uri||coalesce(r.name,'')
Related Questions
Questions on the Jaspersoft forum related to this one include: