2

We're running a simple webapp on WebSphere Liberty, that uses Hibernate as persistence provider (included as a library in the WAR file).

When application is starting up Hibernate is initialized and it will open a connection to DB2 and issue some SQL statements. However, this fails when running on CICS and using JDBC Type 2 Driver DataSource. The following messages are logged (some extra line breaks for readability):

WARN  org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator -
    HHH000342: Could not obtain connection to query metadata : [jcc][50053][12310][4.19.56]
    T2zOS exception: [jcc][T2zos]T2zosCicsApi.checkApiStatus:
       Thread is not CICS-DB2 compatible: CICS_REGION_BUT_API_DISALLOWED ERRORCODE=-4228, SQLSTATE=null
...
ERROR org.hibernate.hql.spi.id.IdTableHelper - Unable obtain JDBC Connection
com.ibm.db2.jcc.am.SqlException: [jcc][50053][12310][4.19.56] T2zOS exception: [jcc][T2zos]T2zosCicsApi.checkApiStatus:
       Thread is not CICS-DB2 compatible: CICS_REGION_BUT_API_DISALLOWED ERRORCODE=-4228, SQLSTATE=null
    at com.ibm.db2.jcc.am.kd.a(Unknown Source) ~[db2jcc4.jar:?]
    ...
    at com.ibm.db2.jcc.t2zos.T2zosConnection.a(Unknown Source) ~[db2jcc4.jar:?]
    ...
    at com.ibm.db2.jcc.DB2SimpleDataSource.getConnection(Unknown Source) ~[db2jcc4.jar:?]
    at com.ibm.cics.wlp.jdbc.internal.CICSDataSource.getConnection(CICSDataSource.java:176) ~[?:?]
    at org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl.getConnection(DatasourceConnectionProviderImpl.java:122) ~[our-app.war:5.1.0.Final]
    at org.hibernate.internal.SessionFactoryImpl$3.obtainConnection(SessionFactoryImpl.java:643) ~[our-app.war:5.1.0.Final]
    at org.hibernate.hql.spi.id.IdTableHelper.executeIdTableCreationStatements(IdTableHelper.java:67) [our-app.war:5.1.0.Final]
    at org.hibernate.hql.spi.id.global.GlobalTemporaryTableBulkIdStrategy.finishPreparation(GlobalTemporaryTableBulkIdStrategy.java:125) [our-app.war:5.1.0.Final]
    at org.hibernate.hql.spi.id.global.GlobalTemporaryTableBulkIdStrategy.finishPreparation(GlobalTemporaryTableBulkIdStrategy.java:42) [our-app.war:5.1.0.Final]
    at org.hibernate.hql.spi.id.AbstractMultiTableBulkIdStrategyImpl.prepare(AbstractMultiTableBulkIdStrategyImpl.java:88) [our-app.war:5.1.0.Final]
    at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:451) [our-app.war:5.1.0.Final]

My current understanding is that when running on CICS and using JDBC Type 2 Drivers only some threads are capable of opening a DB2 connection. That would be the application threads (the ones processing HTTP requests) as well as worker threads servicing CICSExecutorService.

The current solution is to:

  1. Disable JDBC metadata lookup in JdbcEnvironmentInitiator by setting hibernate.temp.use_jdbc_metadata_defaults property to false
  2. Wrap execution of IdTableHelper#executeIdTableCreationStatements in a Runnable and submit it to CICSExecutorService.

Would you consider this solution to be sufficient and suitable for production? Or maybe you use some different approach?

Versions used:

  • CICS Transaction Server for z/OS 5.3.0
  • WebSphere Application Server 8.5.5.8
  • Hibernate 5.1.0

Update: Just to clarify, once our application is started, it can query DB2 with no problems (when servicing HTTP requests). The problem is only related to startup.

Gediminas Rimsa
  • 608
  • 6
  • 16
  • 1
    Check this [Manually configuring a CICS DB2 JDBC type 2 driver data source for Liberty](https://www.ibm.com/support/knowledgecenter/SSGMCP_5.3.0/com.ibm.cics.ts.java.doc/topics/config_db2datasource_liberty.html) – Gas Sep 14 '16 at 15:32
  • @Gas Could you be more specific? I just doublechecked - our DB2 DataSource configuration is exactly the same as in given article (with no properties.db2.jcc specified). – Gediminas Rimsa Sep 15 '16 at 07:05
  • But was the CICS side configured? On that page you have link to configure [CICS region](https://www.ibm.com/support/knowledgecenter/SSGMCP_5.3.0/com.ibm.cics.ts.doc/dfhtk/topics/dfhtk2c.html). I'm not CICS expert, so cannot help you there. – Gas Sep 15 '16 at 07:20
  • @Gas Thanks. The CICS side is configured (can't say if it's done right or not), but in general DB2 access works fine once webapp is started, it is only during startup that we have this problem. Updated the post to clarify that. – Gediminas Rimsa Sep 15 '16 at 09:27

2 Answers2

0

CICS TS v5.3 support for the JPA feature in Liberty was recently made available in a service-refresh (July 2016). Prior to that update, attempting to run JPA in applications would result in very similar problems to those you describe.

Although you are running hibernate and you are on a CICS-enabled thread, it does not have the API environment (which will allow the type 2 JDBC call to succeed). New detection logic was developed specifically (but not exclusively) for use with the DB2 JDBC type 2 driver and JPA. This update was shipped in a recent service refresh and might cure the issues you are seeing.

Try applying: http://www-01.ibm.com/support/docview.wss?crawler=1&uid=swg1PI58375

The description says it is for 'Standard-mode Liberty' support, but it contains other developments as outlined above.

0

The following solution was tested to work ok.

The idea is to execute the SQL/DDL statements using CICSExecutorService#runAsCICS. The following extension is registered via hibernate.hql.bulk_id_strategy property.

package org.hibernate.hql.spi.id.global;

import java.util.concurrent.*;
import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.springframework.util.ClassUtils;
import com.ibm.cics.server.*;

public class CicsAwareGlobalTemporaryTableBulkIdStrategy extends GlobalTemporaryTableBulkIdStrategy {

    @Override
    protected void finishPreparation(JdbcServices jdbcServices, JdbcConnectionAccess connectionAccess, MetadataImplementor metadata, PreparationContextImpl context) {
        execute(() -> super.finishPreparation(jdbcServices, connectionAccess, metadata, context));
    }

    @Override
    public void release(JdbcServices jdbcServices, JdbcConnectionAccess connectionAccess) {
        execute(() -> super.release(jdbcServices, connectionAccess));
    }

    private void execute(Runnable runnable) {
        if (isCics() && IsCICS.getApiStatus() == IsCICS.CICS_REGION_BUT_API_DISALLOWED) {
            RunnableFuture<Void> task = new FutureTask<>(runnable, null);
            CICSExecutorService.runAsCICS(task);
            try {
                task.get();
            } catch (InterruptedException | ExecutionException e) {
                throw new RuntimeException("Failed to execute in a CICS API-enabled thread. " + e.getMessage(), e);
            }
        } else {
            runnable.run();
        }
    }

    private boolean isCics() {
        return ClassUtils.isPresent("com.ibm.cics.server.CICSExecutorService", null);
    }
}

Note that the newer JCICS API version has an overlaod for runAsCics method accepting a Callable, which might be useful to simplify the CICS branch of the execute method to something like this:

CICSExecutorService.runAsCICS(() -> { runnable.run(); return null; }).get();

A few alternatives tried:

  1. Wrapping just the connection acquisition action (org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl#getConnection) did not work as the connection was closed already when it was used in the main thread.
  2. Wrapping the whole application startup (org.springframework.web.context.ContextLoaderListener#contextInitialized) led to classloading issues.

Edit: Eventually went with a custom Hibernate's MultiTableBulkIdStrategy implementation that does not run any SQL/DDL on startup (see project page on GitHub).

Gediminas Rimsa
  • 608
  • 6
  • 16