we are using JBoss EAP 6.2.4 and within a stateless session bean we send JMS messages to an WMQ-queuemanager.
our code is als follows:
#Stateless
#LocalBean
public class MessageSenderBean {
private static ConnectionFactory connectionFactory;
private static InitialContext initialContext;
#EJB
IntegrationPropertyBean ipb;
Logger logger = Logger.getLogger(getClass());
/**
* Default constructor.
*/
public MessageSenderBean() {
}
#PostConstruct
public void postConstruct() {
logger.debug(" MessageSenderBeanPostConstruct called");
try {
initialContext = new InitialContext();
String connectionFactoryName = ipb.getProperty(
MessageSenderBean.class, "connectionFactory");
connectionFactory = (ConnectionFactory) initialContext
.lookup(connectionFactoryName);
} catch (NamingException e) {
logger.error("Exception occurred: " + e.toString());
logger.error(e);
}
}
public String sendMessage(String queueName, String content) {
String result = null;
Connection connection = null;
try {
connection = connectionFactory.createConnection();
} catch (JMSException e) {
logger.error("Exception occurred: " + e.toString());
logger.error(e);
}
// prüfen ob InitialContext leer
try {
if (initialContext == null)
initialContext = new InitialContext();
} catch (NamingException e) {
logger.error("Exception occurred: " + e.toString());
logger.error(e);
}
after startup of the server the bean works perfectly for the first actions but after some time without any action the bean looses the initialContext and an addtional creation fails within the new InitialContext()
Any idea why?
Thanks Joerg
Take in mind the following:
The InitialContext object is not synchronized, which means that an instance should not be accessed simultaneously by different threads.
You have declared the initialContext variable as a class member (static), therefore , at some point different threads will use it simultaneously.
A simple solution is declare the attribute as an instance member.
<!-- language: java -->
private InitialContext initialContext;
Related
I am building a Multitenant saas application using single database with multiple schema; one schema per client. I am using Spring Boot 2.1.5, Hibernate 5.3.10 with compatible spring data jpa and postgres 11.2.
I have followed this blogpost https://dzone.com/articles/spring-boot-hibernate-multitenancy-implementation.
Tried debugging the code, below are my findings:
* For the default schema provided in the datasource configuration, hibernate properly validates schema. It creates the tables/entity in the default schema which are missing or new.
* Tenant Identifier is properly resolved and hibernate builds a session using this tenant.
I have uploaded the code in below repo :
https://github.com/naveentulsi/multitenant-lithium
Some important classes I have added here.
#Component
#Log4j2
public class MultiTenantConnectionProviderImpl implements
MultiTenantConnectionProvider {
#Autowired
DataSource dataSource;
#Override
public Connection getAnyConnection() throws SQLException {
return dataSource.getConnection();
}
#Override
public void releaseAnyConnection(Connection connection) throws SQLException {
connection.close();
}
#Override
public Connection getConnection(String tenantIdentifier) throws SQLException {
final Connection connection = getAnyConnection();
try {
if (!StringUtils.isEmpty(tenantIdentifier)) {
String setTenantQuery = String.format(AppConstants.SCHEMA_CHANGE_QUERY, tenantIdentifier);
connection.createStatement().execute(setTenantQuery);
final ResultSet resultSet = connection.createStatement().executeQuery("select current_schema()");
if(resultSet != null){
final String string = resultSet.getString(1);
log.info("Current Schema" + string);
}
System.out.println("Statement execution");
} else {
connection.createStatement().execute(String.format(AppConstants.SCHEMA_CHANGE_QUERY, AppConstants.DEFAULT_SCHEMA));
}
} catch (SQLException se) {
throw new HibernateException(
"Could not change schema for connection [" + tenantIdentifier + "]",
se
);
}
return connection;
}
#Override
public void releaseConnection(String tenantIdentifier, Connection connection) throws SQLException {
try {
String Query = String.format(AppConstants.DEFAULT_SCHEMA, tenantIdentifier);
connection.createStatement().executeQuery(Query);
} catch (SQLException se) {
throw new HibernateException(
"Could not change schema for connection [" + tenantIdentifier + "]",
se
);
}
connection.close();
}
#Override
public boolean supportsAggressiveRelease() {
return true;
}
#Override
public boolean isUnwrappableAs(Class unwrapType) {
return false;
}
#Override
public <T> T unwrap(Class<T> unwrapType) {
return null;
}
}
#Configuration
#EnableJpaRepositories
public class ApplicationConfiguration implements WebMvcConfigurer {
#Autowired
JpaProperties jpaProperties;
#Autowired
TenantInterceptor tenantInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(tenantInterceptor);
}
#Bean
public DataSource dataSource() {
return DataSourceBuilder.create().username(AppConstants.USERNAME).password(AppConstants.PASS)
.url(AppConstants.URL)
.driverClassName("org.postgresql.Driver").build();
}
#Bean
public JpaVendorAdapter jpaVendorAdapter() {
return new HibernateJpaVendorAdapter();
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource, MultiTenantConnectionProviderImpl multiTenantConnectionProviderImpl, CurrentTenantIdentifierResolver currentTenantIdentifierResolver) {
Map<String, Object> properties = new HashMap<>();
properties.put("hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect");
properties.put("hibernate.hbm2ddl.auto", "update");
properties.put("hibernate.ddl-auto", "update");
properties.put("hibernate.jdbc.lob.non_contextual_creation", "true");
properties.put("show-sql", "true");
properties.put("hikari.maximum-pool-size", "3");
properties.put("hibernate.default_schema", "master");
properties.put("maximum-pool-size", "2");
if (dataSource instanceof HikariDataSource) {
((HikariDataSource) dataSource).setMaximumPoolSize(3);
}
properties.put(Environment.MULTI_TENANT, MultiTenancyStrategy.SCHEMA);
properties.put(Environment.MULTI_TENANT_CONNECTION_PROVIDER, multiTenantConnectionProviderImpl);
properties.put(Environment.MULTI_TENANT_IDENTIFIER_RESOLVER, currentTenantIdentifierResolver);
properties.put(Environment.FORMAT_SQL, true);
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource);
em.setPackagesToScan("com.saas");
em.setJpaVendorAdapter(jpaVendorAdapter());
em.setJpaPropertyMap(properties);
return em;
}
}
#Component
public class TenantResolver implements CurrentTenantIdentifierResolver {
private static final ThreadLocal<String> TENANT_IDENTIFIER = new ThreadLocal<>();
public static void setTenantIdentifier(String tenantIdentifier) {
TENANT_IDENTIFIER.set(tenantIdentifier);
}
public static void reset() {
TENANT_IDENTIFIER.remove();
}
#Override
public String resolveCurrentTenantIdentifier() {
String currentTenant = TENANT_IDENTIFIER.get() != null ? TENANT_IDENTIFIER.get() : AppConstants.DEFAULT_SCHEMA;
return currentTenant;
}
#Override
public boolean validateExistingCurrentSessions() {
return true;
}
}
On successful injection of TenantId by the TenantResolver, the entityManager should be able to store the entities into the corresponding tenant schema in database. That is, if we create an object of an entity and persist same in db, it should be successfully saved in db. But in my case, entities are not getting saved into any schema other than the default one.
Update 1: I was able to do multi-tenant schema switching using mysql 8.0.12. Still not able to do it with postgres.
you should be using AbstractRoutingDataSource to achieve this, it does all the magic behind the scenes, there are many examples online and you can find one at https://www.baeldung.com/spring-abstract-routing-data-source
In your Class "ApplicationConfiguration.java";
You have to remove this "properties.put("hibernate.default_schema", "master");", Why because of when ever your changing the schema it's able to change but when it's reach this line again and again set the default schema
I hope you got the answer
Thank you all
Take care!
trying to find what went wrong with my code which worked fine until i moved to JTAtransactionManager it is having issue saving the record in database but fetching the record working fine, below is my sample TransactionConfig class and service class method.
#Configuration
#ComponentScan
#EnableTransactionManagement
public class TransactionConfig {
#Bean(name = "userTransaction")
public UserTransaction userTransaction() throws Throwable {
UserTransactionImp userTransactionImp = new UserTransactionImp();
//userTransactionImp.setTransactionTimeout(10000);
return userTransactionImp;
}
#Bean(name = "atomikosTransactionManager", initMethod = "init", destroyMethod = "close")
public TransactionManager atomikosTransactionManager() throws Throwable {
UserTransactionManager userTransactionManager = new UserTransactionManager();
userTransactionManager.setForceShutdown(false);
return userTransactionManager;
}
#Bean(name = "transactionManager")
#DependsOn({ "userTransaction", "atomikosTransactionManager" })
public PlatformTransactionManager transactionManager() throws Throwable {
UserTransaction userTransaction = userTransaction();
TransactionManager atomikosTransactionManager = atomikosTransactionManager();
return new JtaTransactionManager(userTransaction, atomikosTransactionManager);
}
}
---Employee Service Class Method---
#Transactional
public void appExample() {
try {
Employee emp = new Employee();
emp.setFirstName("Veer");
emp.setLastName("kumar");
empRepo.save(emp);
} catch (Exception e) {
log.error(e);
}
}
I think issue is with empRepo.save() method call. This call is not committing any changes to database as you are using #Transactional for transaction management.
Please try with empRepo.saveAndFlush() which will immediately flush the data into database. You can refer answer Difference between save and saveAndFlush in Spring data jpa
I've created a Rest service with four methods, GET,POST,UPDATE and DELETE.
These methods make connections to a Database to retrieve and store data.
Now I want to test each method. I've used the Jersey Test Framework for this. And it is working as long as I remove the code what actually makes the call to the database. When I leave the code that makes the call to the database it throws an exception that it could not connect to the database.
EDIT: I have done some research and used dependancy injection. The db calls are moved to a separate class but I'm still doing something wrong.
DatabaseResults. In this class the call to the DB is made.
public class DatabaseResults {
private final String getQuery = "SELECT * FROM movies";
private Connection connection = null;
private PreparedStatement pstmt = null;
private final ArrayList<Movie> jsonList = new ArrayList<>();
public JSONObject getAllMovies() throws SQLException {
try {
ComboPooledDataSource dataSource = DatabaseUtility.getDataSource();
connection = dataSource.getConnection();
pstmt = connection.prepareStatement(getQuery);
ResultSet rs = pstmt.executeQuery();
while (rs.next()) {
jsonList.add(new Movie(rs.getString(1), rs.getString(2), rs.getString(4), rs.getString(3)));
}
} catch (SQLException ex) {
System.out.println(ex);
System.out.println("Could not retrieve a connection");
connection.rollback();
} finally {
connection.close();
}
JSONObject jsonObject = new JSONObject();
jsonObject.put("movies", jsonList);
return jsonObject;
}
}
MoviesResource that contains the REST methods
#Path("movies")
public class MoviesResource {
....
private DatabaseResults dbResults = null;
public MoviesResource() {
this(new DatabaseResults());
}
MoviesResource(DatabaseResults dbr){
this.dbResults = dbr;
}
....
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response getAllMovies() throws JSONException, SQLException {
return Response.status(200).entity(dbResults.getAllMovies().toString()).build();
}
The Test class
#RunWith(MockitoJUnit44Runner.class)
public class MovieResourceTest extends JerseyTest {
JSONObject jsonObject = new JSONObject();
#Mock
DatabaseResults dbr;
#Before
public void setup() throws SQLException{
jsonObject.put("id", "hello");
when(dbr.getAllMovies()).thenReturn(jsonObject);
}
Client client = ClientBuilder.newClient();
WebTarget target = client
.target("http://localhost:9998/RestServiceMovies/resources");
#Override
protected Application configure() {
return new ResourceConfig(MoviesResource.class);
}
#Test
public void getAllMoviesTest() throws SQLException {
String responseGetAllMovies = target("/movies").request().get(String.class);
Assert.assertTrue("hello".equals(responseGetAllMovies));
}
At this moment I can run the tests but still when I test the getAllMovies() method it makes a call to the real database instead of returning the jsonObject.
I have the feeling that a connection is missing between the mock object and the constructor from the MovieResource class?
When you register your resource as a class
new ResourceConfig(MoviesResource.class)
you are telling Jersey to create the instance. If you don't have any DI configured, it will just call the no-arg constructor. In your no-arg constructor, you are just creating the service yourself. It knows nothing about your mock.
What you should do instead is register the resource class as an instance. That way you can pass the mock to the constructor.
MockitoAnnotations.initMocks(this);
return new ResourceConfig()
.register(new MoviesResource(dbr));
Don't use the Mockito runner. Instead use the MockitoAnnotations.initMocks method. That way you control when the #Mocks are injected. If you use the runner, the injection will not happen in time, as the the configure method is called by the framework before the Mockito injection happens.
I have a transactional method at Service layer which call a static method where i am starting a thread for some async task(again some database operation). Does calling a static method from transactional scope can make some db connection leakage problem ?
Below is a sample code explaining the use case.
#Override
#Transactional
public void createRecords(Record r1, Record r2, HttpServletRequest request) {
saveSomeData(r1,r2.getScore());
// Call a Static Method that open a seperate thread
AsyncTasker.doSomeThing(r2);
saveNewData(r2);
}
AsyncTasker.java
public class AsyncTasker {
private static final Logger logger = LoggerFactory.getLogger(AsyncTasker.class);
private static ExecutorService executorService = Executors.newFixedThreadPool(10);
public static void doSomeThing(Record r){
try{
ClearTask clearTask = new ClearTask(r);
executorService.submit(clearTask);
}
catch(Exception e){
logger.error("Error in ClearTask task execution " + r +" : Exception "+ e);
}
}
}
class ClearTask implements Runnable{
private Record r;
ClearTask(Record r){
this.r = r;
}
#Override`enter code here`
public void run() {
Token.deleteAllData(r); // Static method making a rest call
}
}
I am using Play2.1.1 Java with JPA2.0 with hibernate implementation.
to control the transaction by code instead of using #transactional like below is the normal JPA code style, Is there any way to work like below at Play? or how to use JPA.withtranaction() to do? I tried it, no idea how to pass in the parameter, I am not familiar with functional code. thanks a lot. Please give me some sample code based on below.
public void createActorB(final String email, final String psw) throws Throwable {
EntityManager manager = JPA.em();
try {
EntityTransaction ex = manager.getTransaction();
this.dbActor.setEmail(email);
this.dbActor.setCredential(psw);
manager.persist(this.dbActor);
ex.commit();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
throw new ActorException(CODE.UNKNOWN, e);
} finally {
manager.close();
}
}
Now I change my code below to start transaction from service layer, It does not looks efficient, is there any other way to write? thanks
private void internalCreateActor(String email, String psw) throws ActorException {
if (StringUtils.isEmpty(email) || StringUtils.isEmpty(psw))
throw new ActorException(CODE.INVALIDE_PARAMETER);
try {
this.dbActor.setEmail(email);
this.dbActor.setCredential(psw);
this.dbActor.setCreateD(new Date());
JPA.em().persist(this.dbActor);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
throw new ActorException(CODE.UNKNOWN, e);
}
}
public void createActor(final String email, final String psw, final String cellPhone, final Actor.TYPE type)
throws Throwable {
JPA.withTransaction(new Callback0() {
#Override
public void invoke() throws Throwable {
internalCreateActor(email, psw, cellPhone, type);
}
});
}
something like this:
public static User getUserByIdentity(final AuthUserIdentity identity) {
try {
return JPA.withTransaction(new play.libs.F.Function0<User>() {
public User apply() {
return User.findByAuthUserIdentity(identity);
}
});
} catch (Throwable t) {
throw new RuntimeException(t);
}
}
After some time research, I write a method JPAUtil referring to JPA provided by Play which can use normally to control the transaction manually from the service layer actually everywhere.
public class JPAUtil {
static ThreadLocal<EntityManager> currentEntityManager = new ThreadLocal<EntityManager>();
/**
* Get the EntityManager for specified persistence unit for this thread.
*/
public static EntityManager em(String key) {
Application app = Play.application();
if (app == null) {
throw new RuntimeException("No application running");
}
JPAPlugin jpaPlugin = app.plugin(JPAPlugin.class);
if (jpaPlugin == null) {
throw new RuntimeException("No JPA EntityManagerFactory configured for name [" + key + "]");
}
EntityManager em = jpaPlugin.em(key);
if (em == null) {
throw new RuntimeException("No JPA EntityManagerFactory configured for name [" + key + "]");
}
bindForCurrentThread(em);
return em;
}
/**
* Get the default EntityManager for this thread.
*/
public static EntityManager em() {
EntityManager em = currentEntityManager.get();
if (em == null) {
return em(Constants.DATASOURCEKEY);
}
return em;
}
/**
* Bind an EntityManager to the current thread.
*/
public static void bindForCurrentThread(EntityManager em) {
currentEntityManager.set(em);
}
public static void closeEM() {
EntityManager em = currentEntityManager.get();
if (em != null) {
em.close();
}
bindForCurrentThread(null);
}
public static void beginTransaction() {
em().getTransaction().begin();
}
public static void commitTransaction() {
em().getTransaction().commit();
}
}