Home » Eclipse Projects » Eclipse Scout » Feedback from Standalone Client Implementation
Feedback from Standalone Client Implementation [message #1116016] |
Tue, 24 September 2013 22:24 |
Chris Monty Messages: 26 Registered: July 2013 |
Junior Member |
|
|
I just completed a small standalone project which in essence, runs as a desktop application. Jeramie mentioned that feedback would be welcome, so here it is.
Features:- Single user, no authentication, no server (offline implementation)
Since this is a desktop application, a server is unnecessary.
- Custom DB driver (Derby v 10.10.1.1)
I chose Derby because it's portable and stand-alone (no db installation required). I chose version 10.10.1.1 because it supports sequences. I bundled the derby database folder in with the client .zip. Installation is as simple as download, unzip and run.
- Jasper Reports integration
Because the ReportingService shares its connection with the Jasper reporting engine, it's possible to create reports from a database (like Derby) which only supports a single connection.
- Docx4j integration
Just for 'screen scraping' tables into an Excel document.
I had some trouble creating my own derby jdbc connector bundle, even with the help of the tutorial. Most of my time was spent here resolving package import problems. I think that for making your own jdbc bundle you a) have to be well versed with eclipse plugin project architecture, or b) will be by the time you get it working.
Once the whole thing compiled, I faced some issues with the new Sql Style. I had copied the old DerbySqlStyle.java and AbstractDerbySqlService.java, but was having problems with decimals. It seems in Derby, decimal values are typed as java.sql.Types.DOUBLE. The default implementation of org.eclipse.scout.rt.server.services.common.jdbc.style.AbstractSqlStyle.writeBind() supports only DECIMAL and NUMERIC, and refers DOUBLE types to writeDefaultBind() which truncates decimals.
My ugly-but-functional fix is to subclass AbstractSqlStyle and override writeBind() as so:
@Override
public void writeBind(PreparedStatement ps, int jdbcBindIndex, SqlBind bind)
throws SQLException {
switch (bind.getSqlType()) {
case Types.DOUBLE:
// explicit handling for DOUBLE types. The default DOUBLE handling from AbstractSQLService
// does not explicitly specify a scale.
if (bind.getValue() instanceof Double) {
Double value = ((Double)bind.getValue());
// figure out the scale by counting the number of digits after the decimal point
String strVal = value.toString();
int decimalIndex = strVal.indexOf('.');
int scale = (decimalIndex > 0)?(strVal.length() - decimalIndex - 1):0;
// I never need more than 3 decimal places.
if (scale > 3) {
scale = 3;
}
ps.setObject(jdbcBindIndex, value, bind.getSqlType(), scale);
}
break;
default:
super.writeBind(ps, jdbcBindIndex, bind);
}
}
I realised at installation test that my application runs only on 64-bit Windows (not 32-bit). This surprised me, being Java and all. My development environment is 64-bit, as is my version of Java 7. Does anyone know how to remove this constraint?
One final snag I hit was with my Jasper report, which I implemented according to this tutorial. I have a report which has a sub-report. I'm able to load the report as a ByteArrayInputStream, but the report then tries to load the sub-report itself, which it can't find of course. To make matters worse, the report location in my deployment build is inside the plugins folder, which takes on a dynamic name. I've worked around this by making a configuration key with a value like.\\plugins\\<packagename>_<version>.<timestamp>\\reports\\ which gets passed into the report as a parameter (I have to manually update this after each deploy) but it's hardly elegant. Does anyone know how I might find the reports folder at runtime?
But otherwise it's a nice little desktop app.
|
|
|
Re: Feedback from Standalone Client Implementation [message #1116479 is a reply to message #1116016] |
Wed, 25 September 2013 14:01 |
Nils Israel Messages: 72 Registered: May 2010 |
Member |
|
|
Hi Chris,
it is possible to load the subreport the same way as the master report and add the compiled subreport as a parameter:
@Override
public byte[] createCompanyReport(Long companyID) throws ProcessingException {
JasperPrint jasperPrint;
// create a map to pass parameters to the report
HashMap<String, Object> parameter = new HashMap<String, Object>();
parameter.put("COMPANY_NR", companyID);
try {
// load the report from the reports plugin
URL url = new URL("platform:/plugin/org.eclipse.scout.minicrm.reports/reports/company.jrxml");
InputStream inputStream = url.openConnection().getInputStream();
// compile the report
JasperReport report = JasperCompileManager.compileReport(inputStream);
// load the subreport from the reports plugin
URL urlSubreport = new URL("platform:/plugin/org.eclipse.scout.minicrm.reports/reports/subreport.jrxml");
InputStream inputStreamSubreport = urlSubreport.openConnection().getInputStream();
// compile the subreport
JasperReport subreport = JasperCompileManager.compileReport(inputStreamSubreport);
parameter.put("mySubreport", subreport);
// let Jasper Reports use our already defined SQL Connection
jasperPrint = JasperFillManager.fillReport(report, parameter, SQL.getConnection());
// don't create a file but let jasper put the contents in an byte array
ByteArrayOutputStream ba = new ByteArrayOutputStream();
JasperExportManager.exportReportToPdfStream(jasperPrint, ba);
// return the contents to the caller
return ba.toByteArray();
}
catch (JRException e) {
throw new ProcessingException("An error occured during the creation of the report.", e);
}
catch (MalformedURLException e) {
throw new ProcessingException("Can't load the report file.", e);
}
catch (IOException e) {
throw new ProcessingException("An IO-error occured while reading the report file.", e);
}
}
To use the subreport you have to add the parameter
<parameter name="mySubreport" class="net.sf.jasperreports.engine.JasperReport"/>
to the master report and and replace the subreportExpression
<subreportExpression class="net.sf.jasperreports.engine.JasperReport"><![CDATA[$P{mySubreport}]]></subreportExpression>
Kind regards,
Nils
|
|
| | | |
Re: Feedback from Standalone Client Implementation [message #1233461 is a reply to message #1119365] |
Sun, 19 January 2014 18:12 |
Chris Monty Messages: 26 Registered: July 2013 |
Junior Member |
|
|
Here are some further musings I've found whilst working with offline:
I was experiencing some strange behaviour whereby adding database drivers via the Scout Properties dialog (Postgres, MySQL) caused calls to SERVICES.getService(IOfflineDispatcherService.class) to return null. For the record, this was because I had to manually add the com.bsiag.scout.rt.server.jdbc.postgresql9 and org.postgres.postgresql9.jdbc.fragment to my launch configuration, as indicated by console errors. Lesson: always check the console output for "missing bundle" messages.
It's possible to have offline and online product configurations simultaneously. This means you can launch (and even export) the application as a standalone desktop app, or as a client/server appliation. Quite nifty. To do this:- Follow the Standalone Client HowTo tutorial (or use the downloadable offline demo from the tutorial as a starting point).
- Duplicate an existing product configuration (the whole folder containing the .product and the config.ini). Don't forget to update the path to the new config.ini in configuration.
- Add the configuration option offline.enabled=true to the config.ini file.
- In ClientSession.java, change the goOffline() call to:
String offlineMode = Activator.getDefault().getBundle().getBundleContext().getProperty("offline.enabled");
if ("true".equals(offlineMode)) {
ClientSession.get().goOffline();
} This way, offline mode is determinable by configuration.
Incidentally, to get the downloadable how-to-offline-demo working, I had to make the following changes:- Because I'm running x64, I had to update two product dependencies from x86 to x64 (org.eclipse.equinox.security.win32.x86_64 and org.eclipse.swt.win32.win32.x86_64).
- Corrected the location of the config.ini in the offline product configuration.
- Un-commented the server.url parameter. This is required, and a ProcessingException is thrown at AbstractServiceTunnel.java:109 otherwise.
Some things I had to pay particular attention to when configuring offline mode:- In the server plugin.xml extension points, the OfflineDispatcher service extension's factory and session fields must be empty.
- Contrary to comments in this thread, the codes service works fine in offline mode.
|
|
|
Goto Forum:
Current Time: Mon Sep 23 23:01:22 GMT 2024
Powered by FUDForum. Page generated in 0.03628 seconds
|