Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Eclipse Projects » scout » Feedback from Standalone Client Implementation
Feedback from Standalone Client Implementation [message #1116016] Tue, 24 September 2013 22:24 Go to next message
Chris Monty is currently offline Chris Monty
Messages: 24
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 Go to previous messageGo to next message
Nils Israel is currently offline Nils Israel
Messages: 32
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 #1116673 is a reply to message #1116479] Wed, 25 September 2013 18:33 Go to previous messageGo to next message
Chris Monty is currently offline Chris Monty
Messages: 24
Registered: July 2013
Junior Member
Nils, that worked a charm. Thanks for the tip.
Re: Feedback from Standalone Client Implementation [message #1117095 is a reply to message #1116016] Thu, 26 September 2013 05:07 Go to previous messageGo to next message
Jeremie Bresson is currently online Jeremie Bresson
Messages: 763
Registered: October 2011
Senior Member
Chris Monty wrote on Wed, 25 September 2013 00:24
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?


How are you building your application?
- Manually: Export wizard from the Scout Perspective?
- Manually: Export from the Eclipse Product Editor?
- PDE Build?
- Maven Build?

Do you have exported the JRE with your application?
Re: Feedback from Standalone Client Implementation [message #1119365 is a reply to message #1117095] Sat, 28 September 2013 11:07 Go to previous messageGo to next message
Chris Monty is currently offline Chris Monty
Messages: 24
Registered: July 2013
Junior Member
Hi Jeremie, thanks for the response.
Until now I was using the Scout export wizard which automagically does everything. But now that you've asked, I also discovered the Eclipse product export wizard and tried it. I found a tutorial about Products and Deployment, which was helpful.

I tried rewinding the jre from JavaSE-1.7 to JavaSE-1.6 - just in case it was my x64 installation Java 7. I found that when exporting a jre with the application, java rejects all but 64-bit versions with an error message on startup: Quote:
Failed to load the JNI shared Library (...JDK)

Searches on stackoverflow indicate this might lie with the version of eclipse I'm using. Is there a way to generate a 32-bit compatible application, or should I try installing a 32-bit of Eclipse? Or should I look to Maven or PDE for a solution?
Re: Feedback from Standalone Client Implementation [message #1233461 is a reply to message #1119365] Sun, 19 January 2014 18:12 Go to previous message
Chris Monty is currently offline Chris Monty
Messages: 24
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:
  1. Follow the Standalone Client HowTo tutorial (or use the downloadable offline demo from the tutorial as a starting point).
  2. 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.
  3. Add the configuration option offline.enabled=true to the config.ini file.
  4. 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.
Previous Topic:Controlling position of (vertical) scrollbar in the outline
Next Topic:Milestone 3.10.0-M4
Goto Forum:
  


Current Time: Thu Oct 23 09:01:59 GMT 2014

Powered by FUDForum. Page generated in 0.09091 seconds
.:: Contact :: Home ::.

Powered by: FUDforum 3.0.2.
Copyright ©2001-2010 FUDforum Bulletin Board Software