Osuwariboy Osuwariboy - 2 months ago 9
Java Question

Integrating BIRT with Esproc

We've been using Esproc with our BIRT reports for a while now and everything worked perfectly. We followed this tutorial and things worked. However, the latest version of their software incorporated a couple of new functionalities and as such, we now need to upgrade the version running with BIRT. The thing is that now, nothing's working. We keep getting NullPointerException when trying to run reports. This is what we're getting so far:

The following report will be sent to Eclipse:

------
STATUS
------
pluginId org.eclipse.jface
pluginVersion 3.12.0.v20160518-1929
code 2
severity 4
message Problems occurred when invoking code from plug-in: "org.eclipse.jface".
fingerprint eb22eddc61b2abbaef12193bb7441fab

Exception:java.lang.NullPointerException: null
at com.esproc.jdbc.Server.getDfxList(Unknown Source:88)
at com.esproc.jdbc.InternalConnection.getMetaData(Unknown Source:314)
at org.eclipse.birt.report.data.oda.jdbc.ui.provider.JdbcMetaDataProvider.isSupportSchema(JdbcMetaDataProvider.java:305)
at org.eclipse.birt.report.data.oda.jdbc.ui.editors.SQLDataSetEditorPage.createDBMetaDataSelectionComposite(SQLDataSetEditorPage.java:405)
at org.eclipse.birt.report.data.oda.jdbc.ui.editors.SQLDataSetEditorPage.createPageControl(SQLDataSetEditorPage.java:334)
at org.eclipse.birt.report.data.oda.jdbc.ui.editors.SQLDataSetEditorPage.createPageCustomControl(SQLDataSetEditorPage.java:307)
at org.eclipse.datatools.connectivity.oda.design.ui.wizards.DataSetWizardPage.createControl(DataSetWizardPage.java:123)
at org.eclipse.datatools.connectivity.oda.design.internal.ui.DataSetEditorPageCore.createContents(DataSetEditorPageCore.java:74)
at org.eclipse.jface.preference.PreferencePage.createControl(PreferencePage.java:241)
at org.eclipse.birt.report.designer.data.ui.dataset.PropertyPageWrapper.createPageControl(PropertyPageWrapper.java:61)
at org.eclipse.birt.report.designer.data.ui.property.PropertyNode.createPageControl(PropertyNode.java:238)
at org.eclipse.birt.report.designer.data.ui.property.AbstractPropertyDialog.showPage(AbstractPropertyDialog.java:577)
at org.eclipse.birt.report.designer.data.ui.property.AbstractPropertyDialog.showSelectionPage(AbstractPropertyDialog.java:482)
at org.eclipse.birt.report.designer.data.ui.dataset.DataSetEditor.showSelectionPage(DataSetEditor.java:913)
at org.eclipse.birt.report.designer.data.ui.property.AbstractPropertyDialog$2$1.run(AbstractPropertyDialog.java:438)
at org.eclipse.swt.custom.BusyIndicator.showWhile(BusyIndicator.java:70)
at org.eclipse.birt.report.designer.data.ui.property.AbstractPropertyDialog$2.selectionChanged(AbstractPropertyDialog.java:433)
at org.eclipse.jface.viewers.Viewer$1.run(Viewer.java:158)
at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42)
at org.eclipse.ui.internal.JFaceUtil$1.run(JFaceUtil.java:50)
at org.eclipse.jface.util.SafeRunnable.run(SafeRunnable.java:173)
at org.eclipse.jface.viewers.Viewer.fireSelectionChanged(Viewer.java:155)
at org.eclipse.jface.viewers.StructuredViewer.updateSelection(StructuredViewer.java:2191)
at org.eclipse.jface.viewers.StructuredViewer.setSelection(StructuredViewer.java:1728)
at org.eclipse.jface.viewers.TreeViewer.setSelection(TreeViewer.java:1077)
at org.eclipse.jface.viewers.Viewer.setSelection(Viewer.java:383)
at org.eclipse.birt.report.designer.data.ui.property.AbstractPropertyDialog.initTreeSelection(AbstractPropertyDialog.java:408)
at org.eclipse.birt.report.designer.data.ui.property.AbstractPropertyDialog.createDialogArea(AbstractPropertyDialog.java:299)
at org.eclipse.birt.report.designer.data.ui.dataset.DataSetEditor.createDialogArea(DataSetEditor.java:124)
at org.eclipse.jface.dialogs.Dialog.createContents(Dialog.java:767)
at org.eclipse.birt.report.designer.data.ui.dataset.DataSetEditor.createContents(DataSetEditor.java:602)
at org.eclipse.jface.window.Window.create(Window.java:426)
at org.eclipse.jface.dialogs.Dialog.create(Dialog.java:1095)
at org.eclipse.birt.report.designer.ui.dialogs.BaseDialog.open(BaseDialog.java:107)
at org.eclipse.birt.report.designer.data.ui.actions.EditDataSetAction.doAction(EditDataSetAction.java:105)
at org.eclipse.birt.report.designer.internal.ui.views.actions.AbstractElementAction.run(AbstractElementAction.java:70)
at org.eclipse.jface.action.Action.runWithEvent(Action.java:473)
at org.eclipse.jface.action.ActionContributionItem.handleWidgetSelection(ActionContributionItem.java:565)
at org.eclipse.jface.action.ActionContributionItem.lambda$4(ActionContributionItem.java:397)
at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:84)
at org.eclipse.swt.widgets.Display.sendEvent(Display.java:4410)
at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1079)
at org.eclipse.swt.widgets.Display.runDeferredEvents(Display.java:4228)
at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:3816)
at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$4.run(PartRenderingEngine.java:1121)
at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:336)
at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1022)
at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:150)
at org.eclipse.ui.internal.Workbench$5.run(Workbench.java:687)
at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:336)
at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:604)
at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:148)
at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:138)
at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:196)
at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:134)
at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:104)
at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:388)
at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:243)
at sun.reflect.NativeMethodAccessorImpl.invoke0(null:-2)
at sun.reflect.NativeMethodAccessorImpl.invoke(null:-1)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(null:-1)
at java.lang.reflect.Method.invoke(null:-1)
at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:673)
at org.eclipse.equinox.launcher.Main.basicRun(Main.java:610)
at org.eclipse.equinox.launcher.Main.run(Main.java:1519)

------
REPORT
------
anonymousId 12355fbc-cb0f-41c4-b330-1d4a60fd5df2
name
email
comment
eclipseBuildId 4.6.0.I20160606-1100
eclipseProduct org.eclipse.epp.package.reporting.product
javaRuntimeVersion 1.8.0_71-b15
osgiWs win32
osgiOs Windows10
osgiOsVersion 10.0.0
osgiArch x86_64
severity UNKNOWN

-------
BUNDLES
-------
name org.eclipse.birt.report.data.oda.jdbc.ui
version 4.6.0.v201606072122

name org.eclipse.birt.report.data.oda.jdbc
version 4.6.0.v201606072122

name org.eclipse.birt
version 4.6.0.v201606072122

name org.eclipse.birt.report.designer.ui
version 4.6.0.v201606072122

name org.eclipse.birt.report.designer.ui.views
version 4.6.0.v201606072122

name org.eclipse.core.databinding.observable
version 1.6.0.v20160511-1747

name org.eclipse.core.databinding
version 1.6.0.v20160412-0910

name org.eclipse.core.runtime
version 3.12.0.v20160606-1342

name org.eclipse.datatools.connectivity.oda.design.ui
version 3.3.0.201603142002

name org.eclipse.datatools.connectivity.oda.design
version 3.4.0.201603142002

name org.eclipse.datatools.connectivity.oda
version 3.5.0.201603142002

name org.eclipse.datatools.connectivity
version 1.13.0.201603142002

name org.eclipse.e4.ui.workbench
version 1.4.0.v20160517-1624

name org.eclipse.e4.ui.workbench.swt
version 0.14.0.v20160523-1900

name org.eclipse.equinox.app
version 1.3.400.v20150715-1528

name org.eclipse.equinox.launcher
version 1.3.200.v20160318-1642

name org.eclipse.jface
version 3.12.0.v20160518-1929

name org.eclipse.swt
version 3.105.0.v20160603-0902

name org.eclipse.ui
version 3.108.0.v20160518-1929

name org.eclipse.ui.ide.application
version 1.1.100.v20160518-1929

name org.eclipse.ui.ide
version 3.12.0.v20160601-1609


Anyone has any idea what's going on?

Thanks

Answer

Okay, so I was able to actually get things working and the solution isn't exactly what I'd call trivial. I actually had to modify and recompile three BIRT classes. You can easily get the source code here if you don't want to do the Google search. Anyway, our first modification is given to us by the error message cited above. We need to alter JdbcMetaDataProvider.java located in the org.eclipse.birt.report.data.oda.jdbc.ui_4.6.0.v201606072122.jar package. What we're looking for is the isSupportSchema() method. and more specifically this bit of code:

try
{
    return connection.getMetaData( ).supportsSchemasInTableDefinitions( );
}
catch ( SQLException e )
{
    try 
    {
        reconnect( );
        return connection.getMetaData( ).supportsSchemasInTableDefinitions( );
    }
    catch ( Exception e1 )
    {
        try
        {
            ResultSet rs = connection.getMetaData( ).getSchemas( );
            if( rs != null )
                return true;
            else
                return false;
        }
        catch (SQLException e2)
        {
            logger.log( Level.WARNING, e.getMessage( ), e1 );
            return false;
        }
    }
}

Not having access to Esproc's code, I can't exactly say why it does this, but connection.getMetaData( ).supportsSchemasInTableDefinitions( ); throws a NullPointerException. With no "catch" block to treat said exception, Eclipse will just stop its execution and prevent you from accessing your dataset. So we need to fix that like so:

try
{
    return connection.getMetaData( ).supportsSchemasInTableDefinitions( );
}
catch ( SQLException e )
{
    try 
    {
        reconnect( );
        return connection.getMetaData( ).supportsSchemasInTableDefinitions( );
    }
    catch ( Exception e1 )
    {
        try
        {
            ResultSet rs = connection.getMetaData( ).getSchemas( );
            if( rs != null )
                return true;
            else
                return false;
        }
        catch (SQLException e2)
        {
            logger.log( Level.WARNING, e.getMessage( ), e1 );
            return false;
        }
    }
}
catch (NullPointerException e)
{
    return false;
}

Now then, the next two bits of code we need to alter are located in the oda-jdbc.jar package. The first one we're looking for is the Connection.java class and more specifically the method populateConnectionProp( ).

private void populateConnectionProp( ) throws SQLException
{
    if( jdbcConn!= null )
    {
        if( this.autoCommit != null )
            jdbcConn.setAutoCommit( this.autoCommit );
        else
        {
            if  (DBConfig.getInstance().qualifyPolicy(
                        jdbcConn.getMetaData().getDriverName(),
                        DBConfig.SET_COMMIT_TO_FALSE) ) {
                this.autoCommit = false;
                jdbcConn.setAutoCommit( false );
            }
        }
        if( this.isolationMode!= Constants.TRANSCATION_ISOLATION_DEFAULT)
            jdbcConn.setTransactionIsolation( this.isolationMode );
    }
}

This time the culprit throwing a NullPointerException is this line jdbcConn.getMetaData().getDriverName(). Again, not having access to Esproc's code, I don't really know why trying to recover the driver's name doesn't work, but anyway all we need to do is actually catch the exception and things will work normally again:

private void populateConnectionProp( ) throws SQLException
{
    if( jdbcConn!= null )
    {
        if( this.autoCommit != null )
            jdbcConn.setAutoCommit( this.autoCommit );
        else
        {
            try
            {
                if  (DBConfig.getInstance().qualifyPolicy(
                            jdbcConn.getMetaData().getDriverName(),
                            DBConfig.SET_COMMIT_TO_FALSE) ) {
                    this.autoCommit = false;
                    jdbcConn.setAutoCommit( false );
                }
            }
            catch(NullPointerException e)
            {
                this.autoCommit = false;
                jdbcConn.setAutoCommit( false );
            }
        }
        if( this.isolationMode!= Constants.TRANSCATION_ISOLATION_DEFAULT)
            jdbcConn.setTransactionIsolation( this.isolationMode );
    }
}

The other class we're looking for is the CallStatement.java class. In it we're going to have to locate the getCallableParamMetaData() method that I've pasted below:

private java.util.List getCallableParamMetaData( )
{
    java.util.List paramMetaDataList = new ArrayList( );
    try
    {
        DatabaseMetaData metaData = conn.getMetaData( );
        String cataLog = conn.getCatalog( );
        String procedureNamePattern = getNamePattern( this.paramUtil.getProcedure( ) );
        String schemaPattern = null;
        if ( this.paramUtil.getSchema( ) != null )
        {
            schemaPattern = getNamePattern( this.paramUtil.getSchema( ) );
        }

        // handles schema.package.storedprocedure for databases such as
        // Oracle
        if ( !metaData.supportsCatalogsInProcedureCalls( ) )
        {
            if (this.paramUtil.getPackage( ) != null)
            {
                cataLog = getNamePattern( this.paramUtil.getPackage( ) );
            }
        }

        java.sql.ResultSet rs = null;
        rs = metaData.getProcedureColumns( cataLog,
                schemaPattern,
                procedureNamePattern,
                null );
        while ( rs.next( ) )
        {
            ParameterDefn p = new ParameterDefn( );
            p.setParamName( rs.getString( "COLUMN_NAME" ) );
            p.setParamInOutType( rs.getInt( "COLUMN_TYPE" ) );
            p.setParamType( rs.getInt( "DATA_TYPE" ) );
            p.setParamTypeName( rs.getString( "TYPE_NAME" ) );
            p.setPrecision( rs.getInt( "PRECISION" ) );
            p.setScale( rs.getInt( "SCALE" ) );
            p.setIsNullable( rs.getInt( "NULLABLE" ) );
            if ( p.getParamType( ) == Types.OTHER )
                correctParamType( p );
            paramMetaDataList.add( p );
        }
        rs.close( );
    }
    catch ( SQLException e )
    {
        logger.log( Level.SEVERE, "Fail to get SP paramters", e );
    }
    catch( JDBCException ex)
    {
        logger.log( Level.SEVERE, "Fail to get SP paramters", ex );
    }

    return paramMetaDataList;
}

Basically, somewhere within the while ( rs.next( ) ) loop lies the culprit throwing the NullPointerException. So all we need to do to is add a catch statement to treat it as follow:

private java.util.List getCallableParamMetaData( )
{
    java.util.List paramMetaDataList = new ArrayList( );
    try
    {
        DatabaseMetaData metaData = conn.getMetaData( );
        String cataLog = conn.getCatalog( );
        String procedureNamePattern = getNamePattern( this.paramUtil.getProcedure( ) );
        String schemaPattern = null;
        if ( this.paramUtil.getSchema( ) != null )
        {
            schemaPattern = getNamePattern( this.paramUtil.getSchema( ) );
        }

        // handles schema.package.storedprocedure for databases such as
        // Oracle
        if ( !metaData.supportsCatalogsInProcedureCalls( ) )
        {
            if (this.paramUtil.getPackage( ) != null)
            {
                cataLog = getNamePattern( this.paramUtil.getPackage( ) );
            }
        }

        java.sql.ResultSet rs = null;
        rs = metaData.getProcedureColumns( cataLog,
                schemaPattern,
                procedureNamePattern,
                null );
        while ( rs.next( ) )
        {
            ParameterDefn p = new ParameterDefn( );
            p.setParamName( rs.getString( "COLUMN_NAME" ) );
            p.setParamInOutType( rs.getInt( "COLUMN_TYPE" ) );
            p.setParamType( rs.getInt( "DATA_TYPE" ) );
            p.setParamTypeName( rs.getString( "TYPE_NAME" ) );
            p.setPrecision( rs.getInt( "PRECISION" ) );
            p.setScale( rs.getInt( "SCALE" ) );
            p.setIsNullable( rs.getInt( "NULLABLE" ) );
            if ( p.getParamType( ) == Types.OTHER )
                correctParamType( p );
            paramMetaDataList.add( p );
        }
        rs.close( );
    }
    catch ( SQLException e )
    {
        logger.log( Level.SEVERE, "Fail to get SP paramters", e );
    }
    catch( JDBCException ex)
    {
        logger.log( Level.SEVERE, "Fail to get SP paramters", ex );
    }
    catch( NullPointerException ex1)
    {
        logger.log( Level.SEVERE, "Fail to get SP paramters", ex1 );
    }
    return paramMetaDataList;
}

Once you've done your modifications to the files, you actually need to recompile your source code. To do this successfully, you're going to need to copy a couple of jars from Eclipse's plugin folder into the same folder as where you've placed the file you're trying to compile. The jars are given below in the command you need to enter to compile your source code. Here's what you need to enter in order to compile each file:

JdbcMetaDataProvider.java

javac -cp ".;
c:/mypath/org.eclipse.birt.report.data.bidi.utils_4.6.0.v201606072122.jar;
c:/mypath/org.eclipse.birt.report.data.oda.jdbc.ui_4.6.0.v201606072122.jar;
c:/mypath/oda-jdbc.jar;
c:/mypath/org.eclipse.datatools.connectivity.oda_3.5.0.201603142002.jar;
c:/mypath/org.eclipse.datatools.connectivity.oda.design_3.4.0.201603142002.jar;
c:/mypath/org.eclipse.datatools.connectivity.oda.design.ui_3.3.0.201603142002.jar;
c:/mypath/org.eclipse.emf.ecore_2.12.0.v20160420-0247.jar;
c:/mypath/org.eclipse.emf.common_2.12.0.v20160420-0247.jar" JdbcMetaDataProvider.java > log.txt 2>&1

Connection.java

javac -cp ".;
c:/mypath/org.eclipse.birt.report.data.bidi.utils_4.6.0.v201606072122.jar;
c:/mypath/oda-jdbc.jar;
c:/mypath/org.eclipse.datatools.connectivity.oda_3.5.0.201603142002.jar;
c:/mypath/com.ibm.icu_56.1.0.v201601250100.jar" Connection.java > log.txt 2>&1

CallStatement.java

javac -cp ".;
c:/mypath/oda-jdbc.jar;
c:/mypath/org.eclipse.datatools.connectivity.oda_3.5.0.201603142002.jar;
c:/mypath/com.ibm.icu_56.1.0.v201601250100.jar" CallStatement.java > log.txt 2>&1

Now, there's a couple of things that need to be said about the two above commands:

1) They actually need to be entered on one line. I merely broke them down like so for clarity purposes so that each jar needed was easy to spot.

2) This is actually a windows command. If you're running the command on Linux, each jar needs to be separated by a colon(:) instead of a semi-colon(;).

Once the compilation is successful, you'll end up with the following .class files:

JdbcMetaDataProvider$1TempThread.class
JdbcMetaDataProvider$2TempThread.class
JdbcMetaDataProvider.class

Connection$Constants.class
Connection.class

CallStatement.class

These need to be copied back into the original jar files so that they replace the files already there. Luckily, a jar file is nothing more than a zip file so in windows, any unzipping software will easily open things up for you and allow you to navigate to the proper folder.

Here are the the folders in question for each jar:

org\eclipse\birt\report\data\oda\jdbc\ui\provider\
org\eclipse\birt\report\data\oda\jdbc\
org\eclipse\birt\report\data\oda\jdbc\

Once that's done, restart Eclipse and things should work like normal again. I hope this helps someone in the future.

Comments