Search code examples
springjpaeclipselinkmulti-tenant

Concurrency not handled in Eclipse Link when using Spring JpaTransactionManager


We are using Multi Tenant Transaction manager as described in above post dated Registered: November 2014. we are using Spring Data and also we are identifying tenant based on user ID and password, i.e we do not know the tenant first in hand. the application is working fine. We are using spring security with basic authentication. and the application is exposed through Rest API.

The problem occurs when concurrent different tenants use the Transaction manager at the same time. we use TenantResolver class to find the tenant

We have other 2 messaging flow which has around 40 messages and 10 messages per min which uses the connection manager to find the tenant and perform transactions based on that.

The logs here:

MultiTenantJpaTransactionManager.createEntityManagerForTransaction()-> ACTUAL_TENANT : vce 07:51:05,720 INFO [xxx.integration.jms.VehicleRepositoryListener] (vrMessageListenerContainer-1) [] VehicleRepositoryListener.onMessage()--> Process successfully completed in VehicleRepositoryListener 07:51:05,768 ERROR [xxx.integration.services.utils.TenantUtils] (vrMessageListenerContainer-1) [] TenantUtils.setTenantByProductClass -> Error in setting the Tenant org.springframework.orm.jpa.JpaSystemException: Exception [EclipseLink-28013] (Eclipse Persistence Services - 2.6.0.v20150309-bf26070): org.eclipse.persistence.exceptions.EntityManagerSetupException Exception Description: Unable to deploy PersistenceUnit [default] in invalid state [DeployFailed]. Internal Exception: javax.persistence.PersistenceException: Exception [EclipseLink-28019] (Eclipse Persistence Services - 2.6.0.v20150309-bf26070): org.eclipse.persistence.exceptions.EntityManagerSetupException Exception Description: Deployment of PersistenceUnit [default] failed. Close all factories for this PersistenceUnit. Internal Exception: javax.persistence.PersistenceException: Exception [EclipseLink-28009] (Eclipse Persistence Services - 2.6.0.v20150309-bf26070): org.eclipse.persistence.exceptions.EntityManagerSetupException Exception Description: Attempted to redeploy a session named /file:/opt/jboss64/modules/system/layers/base/org/jboss/as/ejb3/main/timers/_default without closing it.; nested exception is javax.persistence.PersistenceException: Exception [EclipseLink-28013] (Eclipse Persistence Services - 2.6.0.v20150309-bf26070): org.eclipse.persistence.exceptions.EntityManagerSetupException Exception Description: Unable to deploy PersistenceUnit [default] in invalid state [DeployFailed]. Internal Exception: javax.persistence.PersistenceException: Exception [EclipseLink-28019] (Eclipse Persistence Services - 2.6.0.v20150309-bf26070): org.eclipse.persistence.exceptions.EntityManagerSetupException Exception Description: Deployment of PersistenceUnit [default] failed. Close all factories for this PersistenceUnit. Internal Exception: javax.persistence.PersistenceException: Exception [EclipseLink-28009] (Eclipse Persistence Services - 2.6.0.v20150309-bf26070): org.eclipse.persistence.exceptions.EntityManagerSetupException Exception Description: Attempted to redeploy a session named /file:/opt/jboss64/modules/system/layers/base/org/jboss/as/ejb3/main/timers/_default without closing it. 07:51:05,774 INFO [xxxintegration.services.XXXUpdateRequestProcessServiceImpl] (xxxMessageListenerContainer-1) [] Returning from XXXUpdateRequestProcessServiceImpl.login() 07:51:05,775 INFO [xxx.integration.services.XXXUpdateRequestProcessServiceImpl] (xxxMessageListenerContainer-1) [] XXXUpdateRequestProcessServiceImpl.getUpdateTechToolResponseDTOs()-> Token in Service :b3b00ab6-5c36-45af-8ec2-0b6f1710e888 07:51:05,777 INFO [xxx.integration.services.VehicleRepositoryService] (vrMessageListenerContainer-1) [] VehicleRepositoryService.parseVehicleRepositoryUpdate()-> Tenant is null 07:51:05,780 INFO [xxx.integration.jms.VehicleRepositoryListener] (vrMessageListenerContainer-1) [] VehicleRepositoryListener.onMessage()--> Process successfully completed in VehicleRepositoryListener 07:51:06,138 INFO [xxx.application.VehicleActivationService] (http-/0.0.0.0:16780-71) [] Inside VehicleActivationService.checkActivation() 07:51:06,141 INFO [xxx.application.VehicleActivationService] (http-/0.0.0.0:16780-71) [] Inside VehicleActivationService.getTenant() 07:51:06,145 ERROR [xxx.integration.rest.UpdateTechTool] (http-/0.0.0.0:16780-71) [] Exception in check activation method : Could not open JPA EntityManager for transaction; nested exception is javax.persistence.PersistenceException: Exception [EclipseLink-28013] (Eclipse Persistence Services - 2.6.0.v20150309-bf26070): org.eclipse.persistence.exceptions.EntityManagerSetupException Exception Description: Unable to deploy PersistenceUnit [default] in invalid state [DeployFailed]. Internal Exception: javax.persistence.PersistenceException: Exception [EclipseLink-28019] (Eclipse Persistence Services - 2.6.0.v20150309-bf26070): org.eclipse.persistence.exceptions.EntityManagerSetupException Exception Description: Deployment of PersistenceUnit [default] failed. Close all factories for this PersistenceUnit. Internal Exception: javax.persistence.PersistenceException: Exception [EclipseLink-28009] (Eclipse Persistence Services - 2.6.0.v20150309-bf26070): org.eclipse.persistence.exceptions.EntityManagerSetupException Exception Description: Attempted to redeploy a session named /file:/opt/jboss64/modules/system/layers/base/org/jboss/as/ejb3/main/timers/_default without closing it. 07:51:06,148 INFO [xxx.application.XXXUpdateTechToolDTOService] (http-/0.0.0.0:16780-71) [] Inside UpdateTechToolDTOService.update() 07:51:06,154 ERROR [xxx.application.XXXUpdateTechToolDTOService] (http-/0.0.0.0:16780-71) [] XXXUpdateTechToolDTOService.update() -> EXCEPTION while calling update :java.lang.NullPointerException 07:51:06,166 INFO [xxx.integration.services.UpdateRequestProcessServiceImpl] (MessageListenerContainer-1) [] Inside UpdateRequestProcessServiceImpl.createResponse() 07:51:06,883 INFO [stdout] (Finalizer) [] [EL Info]: connection: 2017-04-17 07:51:06.882--ServerSession(1485077057)--/file:/opt/jboss64/modules/system/layers/base/org/jboss/as/ejb3/main/timers/_default logout successful 07:51:06,885 INF

Any help or way to handle concurrent issues.

Similar to : EclipseLink - Error: Attempted to redeploy a session without closing it

The MultiTenantJpaTransactionManager class used is:

public class MultiTenantJpaTransactionManager extends JpaTransactionManager {

TenantResolver<Tenant> tenantResolver; 
SchemaProvider schemaProvider; 
@Autowired
public void setTenantResolver(TenantResolver<Tenant> tenantResolver) {
    this.tenantResolver = tenantResolver;
}

@Autowired(required = false)
public void setSchemaProvider(SchemaProvider schemaProvider) {
    this.schemaProvider = schemaProvider;
}

@Override
protected synchronized javax.persistence.EntityManager createEntityManagerForTransaction() {
    boolean refreshed = false;
    EntityManager em = super.createEntityManagerForTransaction();
    try {
        LOGGER.info("Entering createEntityManagerforTransaction()");
        if (schemaProvider != null) {
            TenantSessionCustomizer.setSchemaName(schemaProvider.getSchema());
        }
        ServerSession ss = ((EntityManagerImpl) em.getDelegate()).getServerSession();
        Map sessionProp = ss.getProperties();
        String actualTenant = (String) sessionProp.get(PersistenceUnitProperties.MULTITENANT_PROPERTY_DEFAULT);
        LOGGER.info("MultiTenantJpaTransactionManager.createEntityManagerForTransaction()-> ACTUAL_TENANT : " + actualTenant);

        if (tenantResolver != null && tenantResolver.getCurrentTenant() != null) {
            LOGGER.info("MultiTenantJpaTransactionManager.createEntityManagerForTransaction()-> CURRENT_TENANT : " + tenantResolver.getCurrentTenant());
            if ((actualTenant == null) || (actualTenant != null && !actualTenant.equals(tenantResolver.getCurrentTenant().getHandle()))) {
                sessionProp.put(PersistenceUnitProperties.MULTITENANT_PROPERTY_DEFAULT, tenantResolver.getCurrentTenant().getHandle());
                sessionProp.put(PersistenceUnitProperties.SESSION_CUSTOMIZER, TenantSessionCustomizer.class.getName());
                MetadataRefreshListener mrl = ((EntityManagerImpl) em.getDelegate()).getServerSession().getRefreshMetadataListener();
                if (mrl != null) {
                    Map<String, Object> prop = new HashMap<String, Object>();
                    mrl.triggerMetadataRefresh(prop);
                    refreshed = true;
                    LOGGER.info("createEntityManagerforTransaction()---->Metadata refreshed");
                }
            }
        }
    } catch (Exception ex) {
        LOGGER.error("MultiTenantJpaTransactionManager.createEntityManagerForTransaction()-> Exception while creating EntityManagerForTransaction " + ex.getMessage());
    }
    return refreshed ? super.createEntityManagerForTransaction() : em;
}

}


Solution

  • public class MultiTenantJpaTransactionManager extends JpaTransactionManager { private static final Logger LOGGER = LoggerFactory.getLogger(MultiTenantJpaTransactionManager.class);

    TenantResolver<Tenant> tenantResolver; 
    SchemaProvider schemaProvider;
    
    @Autowired
    public void setTenantResolver(TenantResolver<Tenant> tenantResolver) {
        this.tenantResolver = tenantResolver;
    }
    
    @Autowired(required = false)
    public void setSchemaProvider(SchemaProvider schemaProvider) {
        this.schemaProvider = schemaProvider;
    }
    
    @Override
    protected synchronized javax.persistence.EntityManager createEntityManagerForTransaction() {
        boolean refreshed = false;
        EntityManagerFactory emf = getEntityManagerFactory();
        EntityManager em = super.createEntityManagerForTransaction();
        try {
            LOGGER.info("Entering createEntityManagerforTransaction()");
            if (schemaProvider != null) {
                TenantSessionCustomizer.setSchemaName(schemaProvider.getSchema());
            }
            ServerSession ss = ((EntityManagerImpl) em.getDelegate()).getServerSession();
            Map sessionProp = ss.getProperties();
    
            if (tenantResolver != null && tenantResolver.getCurrentTenant() != null) {
                sessionProp.put(PersistenceUnitProperties.MULTITENANT_PROPERTY_DEFAULT, tenantResolver.getCurrentTenant().getHandle());
    
                em = emf.createEntityManager(sessionProp);
            }
        } catch (Exception ex) {
            LOGGER.error("MultiTenantJpaTransactionManager.createEntityManagerForTransaction()-> Exception while creating EntityManagerForTransaction " + ex.getMessage());
        }
        return em;
    }
    

    }

    Currently testing with the above code it seem to have fixed the Issue.