Used Terms:

JRC – Java Reporting Component

SAP CR – SAP Crystal Reports

First, if your document isn’t very simple one, you definitely need to open your legacy report in SAP Crystal Reports, because it’s nearly impossible to force it to work using just JRC (the error messages are sooo bad, that there is no way you’ll know what is going on wrong).

In other words – JRC will work only if you successfully set the datasource to JavaBean and display “Preview Sample” in SAP CR. Otherwise you get an meaningless error.

In order to achieve it, you have to do several things:

0)SAP CR

Go ahead and get a trial of SAP CR: http://www.sap.com/solution/sme/software/free-trials.html

Now open your legacy report in it.

1)SQL Expression Fields:

/wp-content/uploads/2015/03/1_675300.png

You have to get rid of them, because it’s not working with JavaBeans and POJO.

Of course if you try to run JRC against a report that has this Fields, the JRC will display the meaningless error:

com.crystaldecisions.sdk.occa.report.lib.ReportSDKException: Cannot analyze expression.—- Error code:-2147467259 Error code name:failed

You can remove/edit SQL Expression Fields in SAP CR, but you must do it BEFORE changing the datasource, because afterwards they’re hidden in FieldExplorer (yes, they’re hidden, but they’ll still generate errors…)

/wp-content/uploads/2015/03/2_675301.png

As you can see above, SQL Expression Fields are hidden.

Oh, by the way – if you can not find it under “Find in Formulas” but is still in use, it means that is used in one of the subreports! Good luck in finding it…

2)Subreports:

Remember to perform all of the steps above for each subreport (you can see where they are in “Report Explorer” view)

3)Changing the datasource

In order to change the datasource you have to prepare special JavaBean class for SAP CR Java Bean Connectivity.

If you don’t know how to prepare your SAP CR for JB Connectivity, read my comment here: http://scn.sap.com/docs/DOC-21828

As a ResultSet provider use the following class:


public class MyTables {
  public ResultSet OneOfTheTableNamesUsedInReport() throws Exception {
  return getMockResultSet(OneOfTheTableNamesUsedInReport.class);
  }
  public ResultSet AnotherTableName() throws Exception {
  return getMockResultSet(AnotherTableName.class);
  }
  private MockResultSet getMockResultSet(Class<?> clazz) throws SQLException {
  MockResultSet mockResultSet = new MockResultSet(clazz.getSimpleName());
  Field[] fields = clazz.getDeclaredFields();
  for (Field field : fields) {
  mockResultSet.addColumn(field.getName());
  }
  MockResultSetMetaData metaData = (MockResultSetMetaData)mockResultSet.getMetaData();
  mockResultSet.setResultSetMetaData(metaData);
  for (int i = 0; i < fields.length; i++) {
  Field field = fields[i];
  int seriously_wtf_is_that = i+1;
  metaData.setColumnType(seriously_wtf_is_that, SQLTypeMap.toType(field.getType()));
  }
  return mockResultSet;
  }
  public static class SQLTypeMap {
     public static Class<?> toClass(int type) {
         Class<?> result = Object.class;
         switch (type) {
             case Types.CHAR:
             case Types.VARCHAR:
             case Types.LONGVARCHAR:
                 result = String.class;
                 break;
             case Types.NUMERIC:
             case Types.DECIMAL:
                 result = java.math.BigDecimal.class;
                 break;
             case Types.BIT:
                 result = Boolean.class;
                 break;
             case Types.TINYINT:
                 result = Byte.class;
                 break;
             case Types.SMALLINT:
                 result = Short.class;
                 break;
             case Types.INTEGER:
                 result = Integer.class;
                 break;
             case Types.BIGINT:
                 result = Long.class;
                 break;
             case Types.REAL:
             case Types.FLOAT:
                 result = Float.class;
                 break;
             case Types.DOUBLE:
                 result = Double.class;
                 break;
             case Types.BINARY:
             case Types.VARBINARY:
             case Types.LONGVARBINARY:
                 result = Byte[].class;
                 break;
             case Types.DATE:
                 result = java.sql.Date.class;
                 break;
             case Types.TIME:
                 result = java.sql.Time.class;
                 break;
             case Types.TIMESTAMP:
                 result = java.sql.Timestamp.class;
                 break;
         }
         return result;
     }
     public static int toType(Class<?> clazz) {
         int result = Types.NULL;
         switch (clazz.getName()) {
             case "java.lang.String":
              result = Types.VARCHAR;
                 break;
             case "java.lang.Character":
              result = Types.CHAR;
              break;
             case "java.math.BigDecimal":
                 result = Types.DECIMAL;
                 break;
             case "java.lang.Boolean" :
                 result = Types.BIT;
                 break;
             case "java.lang.Byte":
                 result = Types.TINYINT;
                 break;
             case "java.lang.Short":
                 result = Types.SMALLINT;
                 break;
             case "java.lang.Integer":
                 result = Types.INTEGER;
                 break;
             case "java.lang.Long":
                 result = Types.BIGINT;
                 break;
             case "java.lang.Float":
                 result = Types.FLOAT;
                 break;
             case "java.lang.Double":
                 result = Types.DOUBLE;
                 break;
             case ("[Ljava.lang.Byte"):
                 result = Types.BINARY;
                 break;
             case "java.sql.Date":
                 result = Types.DATE;
                 break;
             case "java.sql.Time":
                 result = Types.TIME;
                 break;
             case "java.sql.Timestamp":
                 result = Types.TIMESTAMP;
                 break;
         }
         return result;
     }
  }
}



IMPORTANT: you have to download the mock-runner library (core and jdbc packages) from here: http://sourceforge.net/projects/mockrunner/files/mockrunner/

Don’t forget to add each of the two jars at the end of the <Classpath> (NOT <JavaBeansClassPath>!) tag, in the SAP CR’s CRConfig.xml file

When you do this, go to the “Database Expert” and add your provider

/wp-content/uploads/2015/03/6_675317.png

Now choose “Set Datasource Location”

/wp-content/uploads/2015/03/3_675318.png

you should now be able to change the Datasource:

/wp-content/uploads/2015/03/4_675319.png

If you see the mapping dialog:

/wp-content/uploads/2015/03/7_675335.png

Go to your class and correct the name or the type of your field, export to JAR, restart SAP CR and try again.

4)When every table is changed without beeing asked about mappings, go to View/Preview Sample

If you succeed, you are ready to go for JRC 😉

To generate PDF using JRC simply use this class:


public class CrystalReportToPDF {
  public static void main(String[] args) {
  try {
  String filename =  "file.rpt";
  ReportClientDocument reportClientDoc = new ReportClientDocument();
  reportClientDoc.open(filename, 0);
  DatabaseController databaseController = reportClientDoc.getDatabaseController();
  databaseController.setDataSource(Arrays.asList(new OneOfTheTables()), OneOfTheTables.class, "OneOfTheTables", "OneOfTheTables");
  databaseController.setDataSource(Arrays.asList(new AnotherTable()), AnotherTable.class, "AnotherTable", "AnotherTable");
  InputStream inputStream = reportClientDoc.getPrintOutputController().export(ReportExportFormat.PDF);
  reportClientDoc.close();
  saveStream(inputStream);
  }
  catch(Exception ex) {
  ex.printStackTrace();
  }
  }
  private static void saveStream(InputStream inputStream) throws IOException {
  File tempFile = File.createTempFile("", ".pdf");
  tempFile.delete();
  Files.copy(inputStream, tempFile.toPath());
  System.out.println("CREATED: "+tempFile.getAbsolutePath());
  }
}



IMPORTANT: use only classes from THIS package: com.crystaldecisions.sdk.occa.report

It is sooooo convenient, that you can find (for example) DatabaseController in three packages:

* com.crystaldecisions.reports.sdk.DatabaseController

* com.crystaldecisions12.sdk.occa.report.application.DatabaseController

* com.crystaldecisions.sdk.occa.report.application.DatabaseController (<—– THIS one works correctly)

If you have Parameter Fields in your legacy report

/wp-content/uploads/2015/03/5_675329.png

you have to put this code just before generating the PDF:


ParameterFieldController paramController = reportClientDoc.getDataDefController().getParameterFieldController();
paramController.setCurrentValue(null,"ParameterName","ParameterValue");



Troubleshooting:

If you forget to add a getter to your POJO, JRC will throw very “meaningful” exception:

com.crystaldecisions.sdk.occa.report.lib.ReportSDKException: java.lang.NullPointerException—- Error code:-2147467259 Error code name:failed

If you obtain this exception:

Error code:-2147213256 Error code name:invalidPOJODataSource

It means that you have not set a datasource for one or more POJOs:

reportClientDoc.getDatabaseController().setDataSource(Arrays.asList(new MyTable()), MyTable.class, “MyTable”, “MyTable”);

Hope this will be helpful.

Don’t forget to like this post 😉

To report this post you need to login first.

Be the first to leave a comment

You must be Logged on to comment or reply to a post.

Leave a Reply