I had created a generic dao with ehcache using spring but i wonder how to use the #Cacheable, #CachePut.
GenericDAO.java
/**
*
*/
package com.breeze.bis.core.service.jdbcTemplate;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.log4j.Logger;
import org.checkthread.annotations.ThreadSafe;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.CallableStatementCreator;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCallback;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.dao.DataAccessException;
/**
* #author peter.wong
*
*/
#Repository
#Qualifier("genericDaoImpl")
#Transactional(propagation=Propagation.REQUIRED, isolation=Isolation.DEFAULT, rollbackFor={Exception.class, SQLException.class, DataAccessException.class}, timeout=9999)
public class GenericDAOImpl implements GenericDAO {
private JdbcTemplate jdbcDao;
private static final Logger logger = Logger.getLogger(GenericDAOImpl.class);
private static final int FETCH_SIZE = 500;
private static final int MAX_ROW = 500;
// =========================================================================================================
/**
*
*/
public GenericDAOImpl() {
}
/**
* #return the jdbcDao
*/
public JdbcTemplate getJdbcDao() {
return jdbcDao;
}
/**
* #param jdbcDao the jdbcDao to set
*/
public void setJdbcDao(JdbcTemplate jdbcDao) {
this.jdbcDao = jdbcDao;
}
/**
*
*/
public void initJDBC() {
jdbcDao.setFetchSize(FETCH_SIZE);
jdbcDao.setMaxRows(MAX_ROW);
}
/* (non-Javadoc)
* #see com.breeze.bis.core.service.jdbcTemplate.GenericDAO#createObject(java.lang.String, java.lang.Object)
*/
#Override
#CachePut(value = "genericDao")
#SuppressWarnings({ "rawtypes", "unchecked" })
#ThreadSafe
public int insert(String sql, Map<Integer, Object> paramMap) {
boolean hasResult = true, hasKey = true;
Integer id = -1;
IStatementExecutor stmtExecutor = new UpdateStatementExecutor(hasKey);
IExtractor integerExtr = new IntegerExtractor(null, hasResult);
IDatabaseExecutor executor = new GenericDatabaseExecutor(stmtExecutor, integerExtr);
PreparedStatementCreator stmtCreator = new GenericPreparedStatementCreator(hasKey, sql, paramMap);
PreparedStatementCallback stmtCallback = new GenericPreparedStatementCallback(executor);
try {
id = this.jdbcDao.execute(stmtCreator, stmtCallback);
} catch (DataAccessException exp) {
logger.error(exp.getMessage());
}
return id;
}
/* (non-Javadoc)
* #see com.breeze.bis.core.service.jdbcTemplate.GenericDAO#update(com.breeze.bis.core.service.jdbcTemplate.GenericPreparedStatementCreator)
*/
#Override
#CachePut(value = "genericDao")
#ThreadSafe
public int update(PreparedStatementCreator stmt) {
int num = -1;
try {
num = this.jdbcDao.update(stmt);
} catch (DataAccessException exp) {
logger.error(exp.getMessage());
}
return num;
}
/* (non-Javadoc)
* #see com.breeze.bis.core.service.jdbcTemplate.GenericDAO#batchUpdate(java.lang.String, java.util.List)
*/
#Override
#CachePut(value = "genericDao")
#ThreadSafe
public int[] batchUpdate(String sql, BatchPreparedStatementSetter stmtSetter) {
int num[] = null;
try {
num = this.jdbcDao.batchUpdate(sql, stmtSetter);
} catch (DataAccessException exp) {
logger.error(exp.getMessage());
}
return num;
}
/* (non-Javadoc)
* #see com.breeze.bis.core.service.jdbcTemplate.GenericDAO#callProcedure()
*/
#Override
#Cacheable(value = "genericDao")
#ThreadSafe
public Map<String, Object> callProcedure(CallableStatementCreator stmt, List<SqlParameter> paramList) {
Map<String, Object> result = null;
try {
result = this.jdbcDao.call(stmt, paramList);
} catch (DataAccessException exp) {
logger.error(exp.getMessage());
}
return result;
}
}
This GenericDAO applies to many tables and how to implement the caching strategy here?
Thanks.
Measure first before implement caching strategy.
Related
I am implementing OAuth2 using spring boot. And followed this example for the same
Spring-oauth2-jpa-example
Video Tutorial
After implementing I have successfully able to generate access token. But the problem is when I try to access protected resource in my case /api. I am getting 404 not found.
Main Method
package com.gatimaanBoot;
import java.util.Arrays;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import com.gatimaanBoot.security.UserRepository;
#SpringBootApplication
public class GatimaanBootApplication {
#Autowired
private PasswordEncoder passwordEncoder;
public static void main(String[] args) {
System.out.println("booting....");
SpringApplication.run(GatimaanBootApplication.class, args);
}
/* #Bean
public CommandLineRunner commandLineRunner(ApplicationContext ctx) {
return args -> {
System.out.println("Let's inspect the beans provided by Spring Boot:");
String[] beanNames = ctx.getBeanDefinitionNames();
Arrays.sort(beanNames);
for (String beanName : beanNames) {
//System.out.println(beanName);
}
};
}*/
/**
* Password grants are switched on by injecting an AuthenticationManager.
* Here, we setup the builder so that the userDetailsService is the one we coded.
* #param builder
* #param repository
* #throws Exception
*/
#Autowired
public void authenticationManager(AuthenticationManagerBuilder builder, UserRepository repository, com.gatimaanBoot.security.service.UserService service) throws Exception {
//Setup a default user if db is empty
if (repository.count()==0)
service.save(new com.gatimaanBoot.security.entities.User("user", "user", Arrays.asList(new com.gatimaanBoot.security.entities.Role("USER"), new com.gatimaanBoot.security.entities.Role("ACTUATOR"))));
builder.userDetailsService(userDetailsService(repository)).passwordEncoder(passwordEncoder);
}
/**
* We return an instance of our CustomUserDetails.
* #param repository
* #return
*/
private UserDetailsService userDetailsService(final UserRepository repository) {
return username -> new CustomUserDetails(repository.findByUsername(username));
}
}
AuthorizationServer
/**
* Copyright 2017 Duronto Technology (P) Limited . All Rights Reserved.
* Duronto Technology PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
package com.gatimaanBoot.security;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
/**
*
* #version 1.0, 09-Jul-2017
* #author Nikhil
*/
/**
* Configures the authorization server.
* The #EnableAuthorizationServer annotation is used to configure the OAuth 2.0 Authorization Server mechanism,
* together with any #Beans that implement AuthorizationServerConfigurer (there is a handy adapter implementation with empty methods).
*/
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private PasswordEncoder passwordEncoder;
/**
* Setting up the endpointsconfigurer authentication manager.
* The AuthorizationServerEndpointsConfigurer defines the authorization and token endpoints and the token services.
* #param endpoints
* #throws Exception
*/
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
}
/**
* Setting up the clients with a clientId, a clientSecret, a scope, the grant types and the authorities.
* #param clients
* #throws Exception
*/
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory().withClient("my-trusted-client")
.authorizedGrantTypes("password","authorization_code","implicit")
.authorities("ROLE_CLIENT","ROLE_TRUSTED_CLIENT").scopes("read","write","trust")
.resourceIds("oauth2-resource").accessTokenValiditySeconds(5000).secret("secret");
}
/**
* We here defines the security constraints on the token endpoint.
* We set it up to isAuthenticated, which returns true if the user is not anonymous
* #param security the AuthorizationServerSecurityConfigurer.
* #throws Exception
*/
#Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()");
}
}
ResourceServer
package com.gatimaanBoot.security;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
#Configuration
#EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.headers().frameOptions().disable().and()
.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/api/**").authenticated();
}
}
Application.properties
#Application Path
server.contextPath = /gatimaanBoot
security.oauth2.resource.filter-order = 3
# Database
spring.datasource.driver = com.mysql.jdbc.Driver
spring.datasource.url = jdbc:mysql://localhost:3306/train?zeroDateTimeBehavior=convertToNull
spring.datasource.username = root
spring.datasource.password = root
# Hibernate
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQLDialect
spring.jpa.properties.hibernate.show_sql= true
spring.jpa.properties.hibernate.hbm2ddl.auto= update
spring.jpa.hibernate.naming-strategy = org.hibernate.cfg.ImprovedNamingStrategy
Controller
/**
* Copyright 2017 Duronto Technology (P) Limited . All Rights Reserved.
* Duronto Technology PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
package com.gatimaanBoot.station.controller;
/**
*
* #version 1.0, 24-Feb-2017
* #author Deepak Bisht
* #author Nikhil Mishra
*
*
*/
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import com.gatimaanBoot.station.dto.StationResponseDTO;
import com.gatimaanBoot.station.model.Station;
import com.gatimaanBoot.station.services.StationService;
#Controller
#RequestMapping("/api/")
public class StationController {
private static final Logger LOGGER = LoggerFactory.getLogger(StationController.class);
#Autowired
StationService stationService;
// get data for particular station
#RequestMapping(value = "/v1.0/station/{stationCode}/", method = RequestMethod.GET)
public #ResponseBody Station getStation(#PathVariable String stationCode) {
Station station = null;
try {
station = stationService.getStation(stationCode);
} catch (Exception e) {
e.printStackTrace();
}
return station;
}
/* Getting List of stations in Json format in pagination */
#RequestMapping(value = "/v1.0/station/", method = RequestMethod.GET)
public #ResponseBody List<Station> getStationList(
#RequestParam(value = "page", required = false, defaultValue = "0") int page) {
List<Station> stationList = null;
try {
stationList = stationService.getStationList(page);
} catch (Exception e) {
LOGGER.debug("Station Controller : " + e.getMessage());
e.printStackTrace();
}
return stationList;
}
// insert new station
#RequestMapping(value = "/v1.0/station/", method = RequestMethod.POST)
public #ResponseBody StationResponseDTO insertStation(#RequestBody Station station) {
StationResponseDTO stationDTO = null;
try {
stationDTO = stationService.insertStation(station);
} catch (Exception e) {
e.printStackTrace();
}
return stationDTO;
}
// insert new station
#RequestMapping(value = "/v1.0/station/", method = RequestMethod.PUT)
public #ResponseBody StationResponseDTO updateStation(#RequestBody Station station) {
StationResponseDTO stationDTO = null;
try {
stationDTO = stationService.updateStation(station);
} catch (Exception e) {
e.printStackTrace();
}
return stationDTO;
}
// delete a station
#RequestMapping(value = "/v1.0/station/", method = RequestMethod.DELETE)
public #ResponseBody StationResponseDTO deleteStation(#RequestBody Station station) {
StationResponseDTO stationDTO = null;
try {
stationDTO = stationService.deleteStation(station);
} catch (Exception e) {
e.printStackTrace();
}
return stationDTO;
}
#RequestMapping(value = "/v1.0/station/list/", method = RequestMethod.POST)
public #ResponseBody List<Station> getTrainList(#RequestBody ArrayList<String> stationList) {
return stationService.getStationListBulk(stationList);
}
}
Thanks.
I am trying to put lock on table while writing on table and if something happened in between then roll-back .
Trying to convert below code
lock table test_g1 read;
lock table test_g write;
-- BEGIN;
START TRANSACTION;
insert into test_g1 values(143);
insert into test_g values(145);
select * from test_g1;
select * from test_g;
Rollback;
select * from test_g;
unlock tables;
How to convert above code into #Transactional spring jdbcTemplate code?
#Transactional(rollbackFor={DataAccessException.class})
public void Test(){
jdbcTemplate.execute("insert into test1 (id, nam) values (4, 'A')");
throw new DataAccessException("error") {
};
}
Here i am trying to throw error, so insert statement should rollback but it's not happening .
Thanks
EDIT-1
I am attaching the code , what exactly i am doing
In JdbcDaoImpl.java , i mentioned my problem as a comment above Test() .
App.java
package com.cgiri.javabrains.Spring4;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.transaction.annotation.Transactional;
public class App
{
public static void main( String[] args )
{
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
App2 app2 = ctx.getBean("app2",App2.class);
app2.call();
}
}
App2.java
package com.cgiri.javabrains.Spring4;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
#Component
public class App2 {
#Autowired
private ApplicationContext ctx = null;
JdbcDaoImpl jdbcDaoImpl ;
public void call( )
{
jdbcDaoImpl = ctx.getBean("jdbcDaoImpl",JdbcDaoImpl.class);
System.out.println(jdbcDaoImpl.getCount());
try{
jdbcDaoImpl.Test();
}catch(Exception e)
{
}
System.out.println(jdbcDaoImpl.getCount());
}
public void setApplicationContext(ApplicationContext context) throws BeansException {
this.ctx = context;
}
}
JdbcDaoImpl.java
package com.cgiri.javabrains.Spring4;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
#Component
#Transactional
public class JdbcDaoImpl {
private JdbcTemplate jdbcTemplate;
private DataSource dataSource;
public DataSource getDataSource() {
return dataSource;
}
#Autowired
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
public int getCount() {
String sql = "SELECT COUNT(*) FROM test1";
// jdbcTemplate.setDataSource(getDataSource());
return jdbcTemplate.queryForObject(sql, Integer.class);
}
public void crateTable() {
String sql = "create table if not exists test1 (id integer, nam char(50))";
jdbcTemplate.execute(sql);
jdbcTemplate.execute("insert into test1 (id, nam) values (1, 'A')");
int count = jdbcTemplate.queryForObject("select count(*) from test1",Integer.class);
System.out.println(count);
}
/**** This is the point where i am trying to rollback insert query ,but it's not happening , instead it's inserting the data into the table and just throwing exception , rollback is not happeneing ****/
#Transactional(rollbackFor={DataAccessException.class})
public void Test(){
jdbcTemplate.execute("insert into test1 (id, nam) values (4, 'A')");
throw new DataAccessException("error") {
};
}
}
AppConfig.java
package com.cgiri.javabrains.Spring4;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.simple.SimpleJdbcCall;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionException;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionTemplate;
#Configuration
#ComponentScan({ "com.cgiri.javabrains.Spring4" })
#PropertySource("classpath:db.properties")
public class AppConfig {
private JdbcTemplate jdbcTemplate;
private TransactionTemplate transactionTemplate;
#Autowired
private Environment env;
#Bean
public BasicDataSource getBasicDataSource()
{
BasicDataSource dao = new BasicDataSource();
dao.setDriverClassName(env.getProperty("db.driverClassName"));
dao.setUrl(env.getProperty("db.url"));
dao.setUsername(env.getProperty("db.userName"));
dao.setPassword(env.getProperty("db.password"));
dao.setInitialSize(2);
dao.setMaxActive(5);
return dao;
}
#Bean
public DataSourceTransactionManager getTransactionManager(BasicDataSource dataSource) {
DataSourceTransactionManager manager = new DataSourceTransactionManager(dataSource);
return manager;
}
#Autowired
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
}
And for locking the table during DML query, if 2 or more people are updating simultaneously on table , this locking mechanism is taken care by mysql server or we have to configure separately for that the way we are doing for transactions ?
Thanks
If you don't want to use #Transactional then you could try to use TransactionTemplate and something like this:
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
#Component
public SimpleDao {
#Autowired
private JdbcTemplate jdbcTemplate;
#Autowired
private TransactionTemplate transactionTemplate;
private void executeTransactionWithoutResult(DbTransactionTask dbTask) {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
#Override
protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
dbTask.executeTask();
}
});
}
public void test() {
DbTransactionTask dbTask = new DbTransactionTask() {
#Override
public void executeTask() {
try {
jdbcTemplate.execute("LOCK TABLES Entry WRITE;");
jdbcTemplate.execute("...");
jdbcTemplate.execute("...");
jdbcTemplate.execute("UNLOCK TABLES;")
} catch (Exception e) {
// Cause rollback of transaction
throw new RuntimeException("Reverting DB operations: " + e.getClass().getSimpleName() + " - " + e.getMessage(), e);
}
}
};
executeTransactionWithoutResult(dbTask);
}
abstract class DbTransactionTask { public abstract void executeTask(); }
}
EDIT: With #Transactional you could try something like this:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.transaction.annotation.Transactional;
import dao.EntryDao;
#SpringBootApplication
public class SpringTransactional {
private ConfigurableApplicationContext springContext;
#Autowired
private EntryDao dao;
public void init() {
springContext = SpringApplication.run(SpringTransactional.class);
springContext.getAutowireCapableBeanFactory().autowireBean(this);
}
public static void main(String[] args) {
SpringTransactional st = new SpringTransactional();
try {
st.init();
dao.db_transaction_test();
} catch (RuntimeException e) {
e.printStackTrace();
} finally {
st.springContext.close();
}
}
}
Where EntryDao is:
package dao;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
/**
* DB creation and schema:
* CREATE DATABASE db_name;
* CREATE USER db_username;
* <p>
* USE db_name;
* GRANT ALL ON db_name.* TO db_username;
* <p>
* SET PASSWORD FOR spz = PASSWORD('username123');
* FLUSH PRIVILEGES;
* <p>
* CREATE TABLE Entry (
* entry_ID INT NOT NULL AUTO_INCREMENT,
* name TEXT NOT NULL,
* <p>
* PRIMARY KEY (entry_ID)
* );
*/
#Component
public class EntryDao {
/**
* application.properties:
* spring.datasource.driver-class-name = com.mysql.jdbc.Driver
* spring.datasource.url = jdbc:mysql://localhost:3306/db_name?useSSL=false&serverTimezone=UTC
* spring.datasource.username = db_username
* spring.datasource.password = username123
*/
#Autowired
private JdbcTemplate jdbcTemplate;
#Transactional
public void db_transaction_test() {
jdbcTemplate.execute("LOCK TABLES Entry WRITE;");
for (int i = 0; i < 10; i++) {
try {
int entry_name = getEntryId("entry_" + i);
System.out.println("Created entry id=" + entry_name);
} catch (EntryDao.DaoException e) {
e.printStackTrace();
}
if (i == 5) {
throw new RuntimeException("Testing data upload procedure break.");
}
}
jdbcTemplate.execute("UNLOCK TABLES;")
}
public int getEntryId(String entryName) throws DaoException {
List<DbEntry> dbEntries = retrieveEntriesFor(entryName);
if (dbEntries.size() == 1) {
return dbEntries.get(0).getEntry_ID();
} else if (dbEntries.size() == 0) {
String sqlInsert = "INSERT INTO Entry (name) VALUES (?)";
jdbcTemplate.update(sqlInsert, entryName);
dbEntries = retrieveEntriesFor(entryName);
if (dbEntries.size() == 1) {
return dbEntries.get(0).getEntry_ID();
} else {
throw new DaoException("Invalid results amount received after creating new (" + dbEntries.size() + ") when getting entry for name: " + entryName);
}
} else {
throw new DaoException("Invalid results amount received (" + dbEntries.size() + ") when getting entry for name: " + entryName);
}
}
private List<DbEntry> retrieveEntriesFor(String entryName) {
return jdbcTemplate.query("SELECT * FROM Entry WHERE name=?;", (ResultSet result, int rowNum) -> unMarshal(result), entryName);
}
private DbEntry unMarshal(ResultSet result) throws SQLException {
DbEntry dbEntry = new DbEntry();
dbEntry.setEntry_ID(result.getInt("entry_ID"));
dbEntry.setName(result.getString("name"));
return dbEntry;
}
public class DbEntry {
private int entry_ID;
private String name;
int getEntry_ID() { return entry_ID; }
void setEntry_ID(int entry_ID) { this.entry_ID = entry_ID; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
}
public class DaoException extends Throwable { DaoException(String err_msg) { super(err_msg); } }
}
I am unable to establish an Atmosphere Broadcaster per user session for an Atmosphere resource. All I can gather from the documentation is how to build "chat" applications that broadcast the same message to every user.
Is it possible to have the Atmosphere framework establish a channel per user session or do I have to do something and handle these connections myself with an in-memory map?
This is the resource I want:
/websockets/notifications
I want user's A and B to connect to this channel from different browsers and then have the ability to stream them messages independently. I should be able to use their session ID's to have atmosphere understand which person to send the response to.
Does Atmosphere support this?
Relevant POM.xml
<spring-boot-starter-web.version>1.3.3.RELEASE</spring-boot-starter-web.version>
<atmosphere-runtime.version>2.4.4</atmosphere-runtime.version>
<atmosphere-javascript.version>2.3.0</atmosphere-javascript.version>
Atmosphere Configuration
package com.hello;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
import org.atmosphere.cache.UUIDBroadcasterCache;
import org.atmosphere.cpr.ApplicationConfig;
import org.atmosphere.cpr.AtmosphereFramework;
import org.atmosphere.cpr.AtmosphereServlet;
import org.atmosphere.cpr.MetaBroadcaster;
import org.springframework.boot.context.embedded.ServletContextInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class AtmosphereConfiguration implements ServletContextInitializer {
#Bean
public AtmosphereServlet atmosphereServlet() {
return new AtmosphereServlet();
}
#Bean
public AtmosphereFramework atmosphereFramework() {
return atmosphereServlet().framework();
}
#Bean
public MetaBroadcaster metaBroadcaster() {
AtmosphereFramework framework = atmosphereFramework();
return framework.metaBroadcaster();
}
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
configureAthmosphere(atmosphereServlet(), servletContext);
}
private void configureAthmosphere(AtmosphereServlet servlet, ServletContext servletContext) {
ServletRegistration.Dynamic atmosphereServlet = servletContext.addServlet("atmosphereServlet", servlet);
atmosphereServlet.setInitParameter(ApplicationConfig.ANNOTATION_PACKAGE, "com.hello");
atmosphereServlet.setInitParameter(ApplicationConfig.BROADCASTER_CACHE, UUIDBroadcasterCache.class.getName());
atmosphereServlet.setInitParameter(ApplicationConfig.BROADCASTER_SHARABLE_THREAD_POOLS, "true");
atmosphereServlet.setInitParameter(ApplicationConfig.BROADCASTER_MESSAGE_PROCESSING_THREADPOOL_MAXSIZE, "10");
atmosphereServlet.setInitParameter(ApplicationConfig.BROADCASTER_ASYNC_WRITE_THREADPOOL_MAXSIZE, "10");
servletContext.addListener(new org.atmosphere.cpr.SessionSupport());
atmosphereServlet.addMapping("/websocket/*");
atmosphereServlet.setLoadOnStartup(0);
atmosphereServlet.setAsyncSupported(true);
}
}
Atmosphere Resource
package com.hello;
import java.nio.charset.StandardCharsets;
import org.atmosphere.config.service.Get;
import org.atmosphere.config.service.Disconnect;
import org.atmosphere.config.service.ManagedService;
import org.atmosphere.config.service.Ready;
import org.atmosphere.cpr.AtmosphereResource;
import org.atmosphere.cpr.AtmosphereResourceEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
#ManagedService(path = NotificationAtmosphereResource.PATH)
public class NotificationAtmosphereResource {
public static final String PATH = "/websocket/notifications";
private Logger logger = LoggerFactory.getLogger(NotificationAtmosphereResource.class);
#Get
public void init(AtmosphereResource resource){
resource.getResponse().setCharacterEncoding(StandardCharsets.UTF_8.name());
}
#Ready
public void onReady(final AtmosphereResource resource) {
logger.info("Connected {}", resource.uuid());
}
#Disconnect
public void onDisconnect(AtmosphereResourceEvent event) {
logger.info("Client {} disconnected [{}]", event.getResource().uuid(),
(event.isCancelled() ? "cancelled" : "closed"));
}
}
Service from which I emit Messages
package com.hello;
import org.atmosphere.cpr.MetaBroadcaster;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
#Service
public class NotificationEmitterBean implements NotificationEmitter {
private Logger logger = LoggerFactory.getLogger(NotificationEmitterBean.class);
#Autowired
private MetaBroadcaster metaBroadcaster;
#Autowired
private NotificationService notificationService;
#Autowired
private JsonMapper jsonMapper;
#Override
public void emitNotification(String sessionId, String msg) {
// This will broadcast to all users on /websocket/notifications
// How can I use sessionId to broadcast to the respective browser?
metaBroadcaster.broadcastTo(NotificationAtmosphereResource.PATH,
jsonMapper.toJson(msg));
}
}
}
The only way I was able to get this working was to create my own session based broadcaster. I used ExcludeSessionBroadcaster written by Jeanfrancois Arcand as my baseline.
IncludeSessionBroadcaster.java
package com.hello;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.Future;
import org.atmosphere.cpr.AtmosphereConfig;
import org.atmosphere.cpr.AtmosphereResource;
import org.atmosphere.cpr.Broadcaster;
import org.atmosphere.cpr.BroadcasterFuture;
import org.atmosphere.cpr.DefaultBroadcaster;
import org.atmosphere.cpr.Deliver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* An implementation of {#link DefaultBroadcaster} that include one or more {#link AtmosphereResource}
*
* Based on ExcludeSessionBroadcaster written by Jeanfrancois Arcand
*
* #author Steven Zgaljic
*/
public class IncludeSessionBroadcaster extends DefaultBroadcaster {
private static final Logger logger = LoggerFactory.getLogger(IncludeSessionBroadcaster.class);
public IncludeSessionBroadcaster(){}
public Broadcaster initialize(String id, AtmosphereConfig config) {
return super.initialize(id, config);
}
/**
* the AtmosphereResource r will be include for this broadcast
*
* #param msg
* #param r
* #return
*/
#Override
public Future<Object> broadcast(Object msg, AtmosphereResource r) {
if (destroyed.get()) {
throw new IllegalStateException("This Broadcaster has been destroyed and cannot be used");
}
Set<AtmosphereResource> sub = new HashSet<AtmosphereResource>();
sub.removeAll(resources);
sub.add(r);
start();
Object newMsg = filter(msg);
if (newMsg == null) {
return null;
}
BroadcasterFuture<Object> f = new BroadcasterFuture<Object>(newMsg, sub.size());
dispatchMessages(new Deliver(newMsg, sub, f, msg));
return f;
}
/**
* the AtmosphereResources subset will be include for this broadcast
*
* #param msg
* #param subset
* #return
*/
#Override
public Future<Object> broadcast(Object msg, Set<AtmosphereResource> subset) {
if (destroyed.get()) {
return futureDone(msg);
}
subset.retainAll(resources);
start();
Object newMsg = filter(msg);
if (newMsg == null) {
return futureDone(msg);
}
BroadcasterFuture<Object> f = new BroadcasterFuture<Object>(newMsg, subset.size());
dispatchMessages(new Deliver(newMsg, subset, f, msg));
return f;
}
/**
* session will be include for this broadcast
*
* #param msg
* #param s
* #return
*/
public Future<Object> broadcast(Object msg, String sessionId) {
if (destroyed.get()) {
return futureDone(msg);
}
Set<AtmosphereResource> subset = new HashSet<AtmosphereResource>();
for (AtmosphereResource r : resources) {
if (!r.getAtmosphereResourceEvent().isCancelled() &&
sessionId.equals(r.getRequest().getSession().getId())) {
subset.add(r);
break;
}
}
start();
Object newMsg = filter(msg);
if (newMsg == null) {
return futureDone(msg);
}
BroadcasterFuture<Object> f = new BroadcasterFuture<Object>(newMsg, subset.size());
dispatchMessages(new Deliver(newMsg, subset, f, msg));
return f;
}
}
Then I assigned this broadcaster to the Atmosphere resource.
NotificationAtmosphereResource.java
package com.hello;
import java.nio.charset.StandardCharsets;
import org.atmosphere.config.service.Get;
import org.atmosphere.config.service.Disconnect;
import org.atmosphere.config.service.ManagedService;
import org.atmosphere.config.service.Ready;
import org.atmosphere.cpr.AtmosphereResource;
import org.atmosphere.cpr.AtmosphereResourceEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
#ManagedService(path = NotificationAtmosphereResource.PATH,
broadcaster=IncludeSessionBroadcaster.class)
public class NotificationAtmosphereResource {
public static final String PATH = "/websocket/notifications";
private Logger logger = LoggerFactory.getLogger(NotificationAtmosphereResource.class);
#Get
public void init(AtmosphereResource resource){
resource.getResponse().setCharacterEncoding(StandardCharsets.UTF_8.name());
}
#Ready
public void onReady(final AtmosphereResource resource) {
logger.info("Connected {}", resource.uuid());
}
#Disconnect
public void onDisconnect(AtmosphereResourceEvent event) {
logger.info("Client {} disconnected [{}]", event.getResource().uuid(),
(event.isCancelled() ? "cancelled" : "closed"));
}
}
Then I could send message to only the browser (sessionId) that I want.
NotificationEmitterBean.java
package com.hello;
import org.atmosphere.cpr.MetaBroadcaster;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
#Service
public class NotificationEmitterBean implements NotificationEmitter {
private Logger logger = LoggerFactory.getLogger(NotificationEmitterBean.class);
#Autowired
private BroadcasterFactory factory;
#Override
public void emitNotification(String sessionId, String msg) {
((IncludeSessionBroadcaster)factory.lookup(NotificationAtmosphereResource.PATH)).broadcast(msg, sessionId);
}
}
}
I built a chat application using the portfolio websocket sample as a guide. I am using spring boot 1.3.3, ActiveMQ, STOMP and the UI is built with KnockoutJs and running on Windows 2012 server.
My issue is after appx 1000 connections to the chat server (spring boot) the server stop accepting any more connections.
I played with different heartbeat settings and also messages size settings etc to no avail.
Did anyone build such a chat / websocket application and is able to achieve more than 1000 concurrent connections?
I spent over a week researching, tweaking the code and I also changed the Windows 2012 server connection limit ( seem like 2012 removed TCP connection limit).
Any help or pointers will be greatly appreciated.
/**
*
*/
package com.test.chat;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.messaging.simp.config.ChannelRegistration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.messaging.simp.config.StompBrokerRelayRegistration;
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.SockJsServiceRegistration;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketTransportRegistration;
import org.springframework.web.socket.server.standard.ServletServerContainerFactoryBean;
import com.test.chat.application.event.StompConnectEvent;
import com.test.chat.application.event.StompConnectedEvent;
import com.test.chat.application.event.StompDisconnectEvent;
/**
* #author pgobin
*
* https://www.youtube.com/watch?v=mmIza3L64Ic
*
*/
#Configuration
#EnableWebSocketMessageBroker
#ComponentScan("com.test.chat")
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
private static final Logger log = Logger.getLogger(WebSocketConfig.class);
#Value("${StompBrokerRelay.host}")
String StompBrokerRelayHost;
#Value("${StompBrokerRelay.port}")
int StompBrokerRelayPort;
#Value("${MessageBroker.User}")
String brokerUser;
#Value("${MessageBroker.Password}")
String brokerPassword;
#Value("${MessageBrokerStompClient.User}")
String stompClientUser;
#Value("${MessageBrokerStompClient.Password}")
String stompClientPassword;
#Value("${sockjs.setHttpMessageCacheSize}")
int sockjs_setHttpMessageCacheSize;
#Value("${sockjs.setStreamBytesLimit}")
int sockjs_setStreamBytesLimit;
#Value("${sockjs.setDisconnectDelay:20000}")
int sockjs_setDisconnectDelay;
#Value("${sockjs.setHeartbeatTime:30000}")
int sockjs_setHeartbeatTime;
// WebSocketTransport settings
#Value("${WebSocketTransportRegistration.MessageSizeLimit:131072}")
int MessageSizeLimit;
#Value("${WebSocketTransportRegistration.SendTimeLimit:15000}")
int SendTimeLimit;
#Value("${WebSocketTransportRegistration.SendBufferSizeLimit:524288}")
int SendBufferSizeLimit;
// ClientOutboundChannel configs
#Value("${ClientOutboundChannel.corePoolSize:25}")
int ClientOutboundChannelcorePoolSize;
#Value("${ClientOutboundChannel.maxPoolSize:50}")
int ClientOutboundChannelmaxPoolSize;
// ClientInboundChannel configs
#Value("${ClientInboundChannel.corePoolSize:25}")
int ClientInboundChannelcorePoolSize;
#Value("${ClientInboundChannel.maxPoolSize:50}")
int ClientInboundChannelmaxPoolSize;
/****
*
*/
#Override
public void configureMessageBroker(MessageBrokerRegistry messageBrokerRegistry)
{
// Destination Prefix - Connect to default in-memory broker
// messageBrokerRegistry.enableSimpleBroker("/topic/", "/queue/");
// connect to AMQ
StompBrokerRelayRegistration broker = messageBrokerRegistry.enableStompBrokerRelay("/queue/", "/topic/");
broker.setRelayHost(StompBrokerRelayHost);
broker.setRelayPort(StompBrokerRelayPort);
broker.setSystemLogin(brokerUser);
broker.setSystemPasscode(brokerPassword);
broker.setClientLogin(stompClientUser);
broker.setClientPasscode(stompClientPassword);
// broker.setVirtualHost(virtualHost)
messageBrokerRegistry.setApplicationDestinationPrefixes("/app");
}
/*****
* https://github.com/rstoyanchev/spring-websocket-test/issues/4
*/
#Override
public void registerStompEndpoints(StompEndpointRegistry stompRegistry)
{
String wsOrigins = AppConfig.getEnv().getProperty("websocket.security.allow.origins", "http://localhost:8080");
log.info("#### ALLOWING MESSAGING ONLY FROM ORIGINS:" + wsOrigins + ". ALL OTHERS WILL BE BLOCKED ####");
String[] cors = StringUtils.split(AppConfig.getEnv().getProperty("websocket.security.allow.origins", "http://localhost:8080"), ",");
// WebSocket URL prefix
SockJsServiceRegistration reg = stompRegistry.addEndpoint("/chat").setAllowedOrigins(cors).withSockJS()
.setStreamBytesLimit(sockjs_setStreamBytesLimit).setDisconnectDelay(sockjs_setDisconnectDelay)
.setHttpMessageCacheSize(sockjs_setHttpMessageCacheSize).setHeartbeatTime(sockjs_setHeartbeatTime).setWebSocketEnabled(true)
.setSupressCors(false);
}
#Bean
public ServletServerContainerFactoryBean createWebSocketContainer()
{
ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
container.setMaxTextMessageBufferSize(8192);
container.setMaxBinaryMessageBufferSize(8192);
container.setAsyncSendTimeout(5000);
container.setMaxSessionIdleTimeout(600000);
return container;
}
#Override
public void configureWebSocketTransport(WebSocketTransportRegistration registration)
{
registration.setMessageSizeLimit(MessageSizeLimit);
registration.setSendTimeLimit(SendTimeLimit);
registration.setSendBufferSizeLimit(SendBufferSizeLimit);
}
/**
* Configure the {#link org.springframework.messaging.MessageChannel} used
* for outgoing messages to WebSocket clients. By default the channel is
* backed by a thread pool of size 1. It is recommended to customize thread
* pool settings for production use.
*/
#Override
public void configureClientOutboundChannel(ChannelRegistration registration)
{
registration.taskExecutor().corePoolSize(ClientOutboundChannelcorePoolSize).maxPoolSize(ClientOutboundChannelmaxPoolSize);
}
#Override
public void configureClientInboundChannel(ChannelRegistration registration)
{
registration.taskExecutor().corePoolSize(ClientInboundChannelcorePoolSize).maxPoolSize(ClientInboundChannelmaxPoolSize);
}
/***
* Intercepts a connect event
*
* #return
*/
#Bean
public StompConnectEvent presenceChannelInterceptorOnConnect(SimpMessagingTemplate messagingTemplate)
{
return new StompConnectEvent(messagingTemplate);
}
/*
* #Bean public StompConnectedEvent
* presenceChannelInterceptorOnConnected(SimpMessagingTemplate
* messagingTemplate) { return new StompConnectedEvent(messagingTemplate); }
*/
#Bean
public StompDisconnectEvent presenceChannelInterceptorOnDisconnect(SimpMessagingTemplate messagingTemplate)
{
return new StompDisconnectEvent(messagingTemplate);
}
}
And my client test code:
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.messaging.simp.stomp.ConnectionLostException;
import org.springframework.messaging.simp.stomp.StompCommand;
import org.springframework.messaging.simp.stomp.StompFrameHandler;
import org.springframework.messaging.simp.stomp.StompHeaders;
import org.springframework.messaging.simp.stomp.StompSession;
import org.springframework.messaging.simp.stomp.StompSessionHandlerAdapter;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.web.socket.WebSocketHttpHeaders;
import org.springframework.web.socket.client.standard.StandardWebSocketClient;
import org.springframework.web.socket.messaging.WebSocketStompClient;
import org.springframework.web.socket.sockjs.client.SockJsClient;
import org.springframework.web.socket.sockjs.client.Transport;
import org.springframework.web.socket.sockjs.client.WebSocketTransport;
public static void runTest(final long userUid, final int clientNum)
{
//String stompUrl = "ws://localhost:8080/chat";
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
taskScheduler.afterPropertiesSet();
StandardWebSocketClient webSocketClient = new StandardWebSocketClient();
List<Transport> transports = new ArrayList<>();
transports.add(new WebSocketTransport(webSocketClient));
SockJsClient sockJsClient = new SockJsClient(transports);
WebSocketStompClient stompClient = new WebSocketStompClient(sockJsClient);
// stompClient.setMessageConverter(new StringMessageConverter());
stompClient.setMessageConverter(new org.springframework.messaging.converter.MappingJackson2MessageConverter());
stompClient.setTaskScheduler(taskScheduler);
stompClient.setDefaultHeartbeat(new long[] { 0, 0 });
ConsumerStompSessionHandler handler = new ConsumerStompSessionHandler(BROADCAST_MESSAGE_COUNT, connectLatch, subscribeLatch, messageLatch,
disconnectLatch, failure, clientNum);
HashMap<String, Object> params = new HashMap<String, Object>();
params.put("userid", userUid);
WebSocketHttpHeaders wsHeaders = new WebSocketHttpHeaders();
wsHeaders.add("userid", "" + userUid);
StompHeaders stompHeaders = new StompHeaders();
stompHeaders.add("userid", "" + userUid);
stompHeaders.add("channelID", "java-" + System.currentTimeMillis());
stompHeaders.add("platform", "Windows");
stompHeaders.add("clientIP", "10.1.1.1");
// stompClient.connect(stompUrl, handler, params);
stompClient.connect(stompUrl, wsHeaders, stompHeaders, handler, params);
}
private static class ConsumerStompSessionHandler extends StompSessionHandlerAdapter {
private final int expectedMessageCount;
private final CountDownLatch connectLatch;
private final CountDownLatch subscribeLatch;
private final CountDownLatch messageLatch;
private final CountDownLatch disconnectLatch;
private final AtomicReference<Throwable> failure;
private AtomicInteger messageCount = new AtomicInteger(0);
int clientNum = 0;
public ConsumerStompSessionHandler(int expectedMessageCount, CountDownLatch connectLatch, CountDownLatch subscribeLatch,
CountDownLatch messageLatch, CountDownLatch disconnectLatch, AtomicReference<Throwable> failure, int clientNum)
{
this.expectedMessageCount = expectedMessageCount;
this.connectLatch = connectLatch;
this.subscribeLatch = subscribeLatch;
this.messageLatch = messageLatch;
this.disconnectLatch = disconnectLatch;
this.failure = failure;
this.clientNum = clientNum;
}
#Override
public void afterConnected(final StompSession session, StompHeaders connectedHeaders)
{
__ActiveConn = __ActiveConn + 1;
this.connectLatch.countDown();
session.setAutoReceipt(true);
final RequestUserList req = new RequestUserList();
req.setCustomerid(customerID);
String channelID = System.currentTimeMillis() + "";
String subscribeChannel = __SUBSCRIBE_PREDICATE_QUEUE + channelID;
final String sendChannel = __SEND_PREDICATE + "userListOnline";
req.setChannelID(channelID);
// session.send(sendChannel, req);
// System.out.println("Client " + clientNum + " connected");
session.subscribe(subscribeChannel, new StompFrameHandler() {
#Override
public Type getPayloadType(StompHeaders headers)
{
System.out.println("Got ResponseH");
return String.class;
}
#Override
public void handleFrame(StompHeaders headers, Object payload)
{
System.out.println("Got ResponseA");
/* if (messageCount.incrementAndGet() == expectedMessageCount)
{
messageLatch.countDown();
disconnectLatch.countDown();
session.disconnect();
}*/
}
}).addReceiptTask(new Runnable() {
#Override
public void run()
{
System.out.println("Got Response for client " + clientNum);
//subscribeLatch.countDown();
}
});
// session.send(sendChannel, req);
}
#Override
public void handleTransportError(StompSession session, Throwable exception)
{
__ErrorConn = __ErrorConn + 1;
logger.error("Transport error", exception);
this.failure.set(exception);
if (exception instanceof ConnectionLostException)
{
this.disconnectLatch.countDown();
}
}
#Override
public void handleException(StompSession s, StompCommand c, StompHeaders h, byte[] p, Throwable ex)
{
logger.error("Handling exception", ex);
this.failure.set(ex);
}
#Override
public void handleFrame(StompHeaders headers, Object payload)
{
System.out.println("Got ResponseF");
Exception ex = new Exception(headers.toString());
logger.error("STOMP ERROR frame", ex);
this.failure.set(ex);
}
#Override
public String toString()
{
return "ConsumerStompSessionHandler[messageCount=" + this.messageCount + "]";
}
}
public static void main(String[] args)
{
try
{
int clientCount = 3000;
for (int x = 0; x < clientCount; x++){
runTest(121807, x+1);
}
System.out.println("DONE...");
System.out.println("Live Connections = " + __ActiveConn);
System.out.println("Error Connections = " + __ErrorConn);
/*
for (int x = 0; x < clientCount; x++)
{
final int clientNum = x;
ThreadPoolManager.executorService.execute(new Runnable() {
#Override
public void run()
{
try
{
Thread.sleep(500);
} catch (Exception e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
runTest(121807, clientNum);
}
});
}
*/
System.out.println("DONE..Waiting..");
Thread.sleep(1000000);
} catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
i need help with this kind of issue:
Exception attempting to inject Unresolved Message-Destination-Ref web.News/queue#java.lang.String#null into class web.News: Lookup failed for 'java:comp/env/web.News/queue' in SerialContext[myEnv={java.naming.factory.initial=com.sun.enterprise.naming.impl.SerialInitContextFactory, java.naming.factory.state=com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl, java.naming.factory.url.pkgs=com.sun.enterprise.naming}
This is my Entity Class
package ejb;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
/**
*
* #author Maciej1
*/
#Entity
public class NewsItem implements Serializable {
private String heading;
private String body;
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#Override
public int hashCode() {
int hash = 0;
hash += (id != null ? id.hashCode() : 0);
return hash;
}
#Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof NewsItem)) {
return false;
}
NewsItem other = (NewsItem) object;
if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
return false;
}
return true;
}
#Override
public String toString() {
return "ejb.NewsItem[ id=" + id + " ]";
}
/**
* #return the heading
*/
public String getHeading() {
return heading;
}
/**
* #param heading the heading to set
*/
public void setHeading(String heading) {
this.heading = heading;
}
/**
* #return the body
*/
public String getBody() {
return body;
}
/**
* #param body the body to set
*/
public void setBody(String body) {
this.body = body;
}
}
here is my Message-Driven Beans class:
package ejb;
import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.ObjectMessage;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
/**
*
* #author Maciej1
*/
#MessageDriven(mappedName = "jms/NewsQueue", activationConfig = {
#ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue")
})
public class NewsMDB implements MessageListener {
#PersistenceContext(unitName = "MDBLabPU")
private EntityManager em;
public NewsMDB() {
}
#Override
public void onMessage(Message message) {
ObjectMessage msg = null;
try {
if (message instanceof ObjectMessage) {
msg = (ObjectMessage) message;
NewsItem e = (NewsItem) msg.getObject();
saveObject(e);
}
} catch (JMSException e) {
e.printStackTrace();
}
}
public void saveObject(Object object) {
em.persist(object);
}
}
In the end my class to communication with JavaServer Faces:
package web;
import ejb.NewsItem;
import ejb.NewsItemFacadeLocal;
import java.util.List;
import javax.annotation.Resource;
import javax.ejb.EJB;
import javax.enterprise.context.RequestScoped;
import javax.inject.Named;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.ObjectMessage;
import javax.jms.Session;
/**
*
* #author Maciej1
*/
#Named
#RequestScoped
public class News {
#EJB
private NewsItemFacadeLocal newsItemFacade;
#Resource(lookup="java:comp/DefaultJMSConnectionFactory")
private ConnectionFactory connectionFactory;
#Resource(lookup="jms/NewsQueue")
private javax.jms.Queue queue;
private String headingText = "headingText";
private String bodyText = "bodyText";
void sendNewsItem(String heading, String body)
{
try {
Connection connection = connectionFactory.createConnection();
Session session;
session = connection.createSession(true, 0);
MessageProducer messageProducer = session.createProducer(queue);
ObjectMessage message = session.createObjectMessage();
NewsItem e = new NewsItem();
e.setHeading(heading);
e.setBody(body);
message.setObject(e);
messageProducer.send(message);
messageProducer.close();
connection.close();
} catch (JMSException
ex) {
ex.printStackTrace();
}
}
public List<NewsItem> getNewsItems()
{
return newsItemFacade.findAll();
}
public String submitNews(){
sendNewsItem(getHeadingText(), getBodyText());
return null;
}
Can you help me with this issue?
I discover that problem can be probable in Faces file:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html">
<h:head>
<title>Facelet Title</title>
</h:head>
<h:body>
<h:form>
<h:outputText></h:outputText>
<h:inputText value="#{news.headingText}" id="headingInputText"></h:inputText>
<h:outputText></h:outputText>
<h:inputText value="#{news.bodyText}" id="bodyInputText"></h:inputText>
<h:commandButton value="Submit" id="submitButton"></h:commandButton>
</h:form>
</h:body>
</html>
I had to change from this declaration:
#Resource(lookup="jms/NewsQueue")
private javax.jms.Queue queue;
to this:
#Resource(lookup="java:app/jms/NewsQueue")
private javax.jms.Queue queue;
It's generate from NetBeans 8.1 probably differently. Now this code work perfectly