I searched for a couple of days to fix this bug with nothing new.
I had a report which include multi-level subreports everything works fine on iReport 3.7.5
. I used subreport.jasper
as subreport expression in the first level & also subreportA.jasper
, subreportB.jasper
in the second level & place all (the main report & subreports) in the same path.
The problem raised when I try to deploy it on my JasperServer
.
When I try to upload the first main report the iReport
wizard offerd me to attach the first subreport.jrxml
in resource folder and access it with repo:subreport.jrxml
or repo:subreport.jasper
.
Then I manually upload the second level subreports and do the same thing change the subreport expression to repo:subreportA.jasper
and repo:subreportB.jasper
.
I got compilation error : Unable to locate the subreport with expression: ""repo:subreport.jasper"". java.lang.Exception: repo:subreport.jrxml not found.
I try dozen solution and nothing works. using : SUBREPORT_DIR @ the beginning,
using full path : repo:/Circuit_Reports/Connectivity/Connectivity_files/
,
switch between .jasper
& .jrxml
.
using jasperserver_api_engine_impl_0_fix.jar
in lib folder as a fixation to this bug,
I also searched the database record to be sure that they are in the same folder and have the same parent folder.
(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)
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 ;-) ):
Project->Properties->Java Build Path->Libraries->Add
)../jasperserver/WEB-INF/lib/
folderjr.utl.EnvScriptlet
that does the ugly subreport path/reference magic in your master reports
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
jr.utl.env
property (prod, myOsUsrName, test, demo, staging, local, ...)
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)
jr.utl.EnvScriptlet.getSubrepPath( $P{BASE_DIR}, "subrep1.jrxml")
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)(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:
REPORT_SCRIPTLET
or scriptlet functionality may not be the best way to go, but it will probably work in the vast majority of use casesFileResolver
RepositoryUtil
)(5: the relevant special handling is encoded here: EnvScriptlet.java/getSubrepPath(String,String,boolean,String[]))
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:
C:\eclipse\
C:\workspace\
C:\workspace\report-project\
C:\workspace\report-project\src/reports
C:\workspace\report-project\src/reports/masterrep.jrxml
C:\workspace\report-project\src/reports/subrep1.jrxml
C:\workspace\report-project\src/reports/somesubdir/subrep2.jrxml
BASE_DIR
(explained in next section) in our workspace master report is set to C:\workspace\report-project\src/reports/
/x/y/z/
(which is not to-be-confused with the visual named-path, e.g. which could be Financial Reports/Expenses/Current Year
)(and other "Jasper runtime environments" like custom Java Jasper package usage):
BASE_DIR
/
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
$P{BASE_DIR} + "subrep1.jrxml"
which should resolve to
repo:subrep1.jrxml
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)
(the official IReport successor with loads of more functionality)
(if you do not use the preview functionality this may be uninteresting to you)
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
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)Eclipse->Window->Preferences->JasperStudio->Report Execution->Virtualizer Temporary Path
is something unrelated (not useful here) dealing with the storage of the report result "caching"*.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)
(e.g. provided by some Tomcat application server and storing its data in some postgres database)
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)BASE_DIR
to repo:
in the to-be-uploaded master report
$P{BASE_DIR} + "subrep1.jrxml"
and $P{BASE_DIR} + "somesubdir/subrep2.jrxml"
should work on the server as wellrepo:/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)
suppose we upload our subreports to the master repo id-path /x/y/z/
(as shown on top)
again we have to differentiate two different use cases
we do NOT want to use the subreport as a standalone report (it will always only be included in other master reports)
in this case we should upload it using Add Resource->File->JRXML
and reference it
../subrep1.jrxml
or ./subrep1.jrxml
do not work since it seems the underlying logic cannot handle the relative path expression ..
(and likely .
not as well) (which would actually be nice :-( )
so what we have to do here is to supply an absolute canonical path in the BASE_DIR
of our masterrep.jrxml, e.g. repo:/x/y/z/
we want to use the subreport as a standalone report as well
in this case we should upload it using Add Resource->JasperReport
this obviously creates a hidden folder repo:/x/y/z/subrep1.jrxml_files
containing the report itself and other resources
that's why we not only have to adjust the BASE_DIR
(as above), but also the subreport expression to, e.g. $P{BASE_DIR} + "subrep1.jrxml_files/subrep1.jrxml_"
(which points to the subreport itself)
and maybe remove the net.sf.jasperreports.engine.JasperCompileManager.compileReport(...)
wrapper function, because the server does this automatically for *.jrxml
files
I did not fully investigate some other likely incorrectly used approaches which did not work for me to solve the mentioned problems (maybe somebody else has some outcome/corrections here):
$P{REPORT_FILE_RESOLVER}.resolveFile("subrep1.jrxml")
(NullPointerException)$P{REPORT_CONTEXT}.getRealPath("subrep1.jrxml")
$P{REPORT_CONTEXT}.getProperty("REPORT_FILE_RESOLVER").resolveFile("subrep1.jrxml")
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,'')
Questions on the Jaspersoft forum related to this one include:
Not sure if this mechanism works in all cases but it certainly works for JasperSoft Studio 5.6.0
and Jasper Reports Server 5.6.0
.
Essentially we need a simple way to detect that we are running on the server - I use the presence (or absence) of the $P{REPORT_CONTEXT}
parameter which experiments show is present on the server but not present during preview.
<parameter name="OnServer" class="java.lang.Boolean" isForPrompting="false">
<parameterDescription><![CDATA[Are we running on server]]></parameterDescription>
<defaultValueExpression><![CDATA[Boolean.valueOf($P{REPORT_CONTEXT}!=null)]]></defaultValueExpression>
</parameter>
Once you have that you can then define the location of your subreport from a choice of two:
<parameter name="SubReportProducts" class="java.lang.String" isForPrompting="false">
<parameterDescription><![CDATA[The products subreport]]></parameterDescription>
<defaultValueExpression><![CDATA[$P{OnServer}.booleanValue() ? "repo:OrderPicksheetProducts.jrxml" : "OrderPicksheetProducts.jasper"]]></defaultValueExpression>
</parameter>
And then include the sub report:
<subreportExpression><![CDATA[$P{SubReportProducts}]]></subreportExpression>
You can then use Preview
in studio and all still works when you deploy to server.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With