I've got an issue involving scheduled tasks and Hibernate. I'm trying to use the java Timer object to schedule a task to run once a second. That tasks involves querying a database via hibernate. Now, as far as I understand it, Hibernate getCurrentSession() method will return a session bound to the current context.
Firstly, I am using this code to schedule the task:
timer = new Timer();
task = new UpdateTask();
// Schedule the task for future runs.
timer.scheduleAtFixedRate(task, 1000, 1000);
The code for the task is as follows:
public void run() {
FilterMeetingDao fmService = new FilterMeetingDao();
Set<String> codes = new HashSet<String>();
String date = new SimpleDateFormat(Constants.DATE_FORMAT).format(new Date());
try {
List<Meeting> meetings = new MeetingDao().getMeetings(date);
for(Meeting m : meetings)
{
if(RawMeetingFilter.isDefaultMeeting(m)) {
// Is a default meeting. Insert into the database.
codes.add(m.getCode());
}
}
fmService.add(codes, date);
} catch (ParseException e) {
e.printStackTrace();
}
}
Finally, here is the code is the DAO object that is retrieving the information:
public List<Meeting> getMeetings(String date) throws ParseException{
SimpleDateFormat sdf = new SimpleDateFormat(Constants.DATE_FORMAT);
Date startDate = sdf.parse(date);
Query query = getSession().createQuery("from Meeting m where m.startDate = :startDate and source not like 'TTV' order by countrycode, categorycode, description");
query.setParameter("startDate", startDate);
return query.list();
}
And the getSession method is the origin of the NPE, which is as follows:
public Session getSession(){
return sessionFactory.getCurrentSession();
}
The line return sessionFactory.getCurrentSession(); is the origin of the error. Now, this obviously means the sessionFactory is null. However, in my code, the exact same database request is made in the previous line. This tells me that the sessionFactory isn't null because the previous request is successful.
Here is a stack trace of the NullPointerException:
Exception in thread "Timer-0" java.lang.NullPointerException
at com.sis.rawfilter.dao.impl.BaseDao.getSession(BaseDao.java:13)
at com.sis.rawfilter.dao.impl.MeetingDao.getMeetings(MeetingDao.java:21)
at com.sis.rawfilter.domain.UpdateTask.run(UpdateTask.java:32)
at java.util.TimerThread.mainLoop(Timer.java:555)
at java.util.TimerThread.run(Timer.java:505)
Just for reference..
meetings = meetingService.getMeetings(date);
// meetingService is the wrapper for the DAO object. this is the successful request.
And this is how I start my request:
us.startTimer();
Which starts off the call chain, with the timer code at the top.
Edits I've made to try and fix it
So I added in a new bean tag into the applicationContext.xml file. That looks like this:
<bean id="updateTask" class="com.sis.rawfilter.domain.UpdateTask"/>
And I've added in the Autowired tag into the class for the fields:
#Autowired
private IMeetingService meetingService;
#Autowired
private IFilterMeetingService filterMeetingService;
These types are declared in the applicationContext file as:
<bean id="meetingService" class="com.sis.rawfilter.service.impl.MeetingService"/>
<bean id="filterMeetingService" class="com.sis.rawfilter.service.impl.FilterMeetingService"/>
Sample Service Class
#Transactional
public class FilterMeetingService implements IFilterMeetingService {
#Autowired
private IFilterMeetingDao filterMeetingDao;
public List<FilterMeeting> getFilterMeetings(String date) throws ParseException{
return filterMeetingDao.getFilterMeetings(date);
}
public void save(Set<String> selectedMeetings, Set<String> excludedMeetings, String date) throws ParseException{
if(excludedMeetings.size() > 0){
filterMeetingDao.remove(excludedMeetings, date);
}
if(selectedMeetings.size() > 0){
filterMeetingDao.add(selectedMeetings, date);
}
}
public void setFilterMeetingDao(IFilterMeetingDao filterMeetingDao) {
this.filterMeetingDao = filterMeetingDao;
}
}
Sample Dao Class
public class FilterMeetingDao extends BaseDao implements IFilterMeetingDao {
#SuppressWarnings("unchecked")
public List<FilterMeeting> getFilterMeetings(String date) throws ParseException{
SimpleDateFormat sdf = new SimpleDateFormat(Constants.DATE_FORMAT);
Date startDate = sdf.parse(date);
Query query = getSession().createQuery("from FilterMeeting fm where fm.startDate = :startDate");
query.setParameter("startDate", startDate);
return query.list();
}
public void remove(Set<String> codes, String date){
Query query = getSession().createSQLQuery("delete from tbl where d = :date and c in :codes ");
query.setParameter("date", date);
query.setParameterList("codes", codes);
query.executeUpdate();
}
public void add(Set<String> codes, String date) throws ParseException{
SimpleDateFormat sdf = new SimpleDateFormat(Constants.DATE_FORMAT);
for(String code : codes){
FilterMeeting filterMeeting = new FilterMeeting(code, sdf.parse(date), Config.getInstance().getQueue());
getSession().save(filterMeeting);
}
}
}
You are creating new object of meeting dao
new MeetingDao().getMeetings(date);
so sessionFactory object will not initialize and obviously you will get nullPointerException, you should Autowired dao.
Related
I have written a simple spring batch project where
API to execute job: returns job ID on job launch
reads/processes/writes from/to DB in multithread parallel processing
(Launching the job asynchronously to get the job ID in advance so I can poll the status of the job from another API request.)
API to poll the status of the job with respect to the job ID passed.
Polling api works smoothly if job step's throttle limit is 7 or less.
However, if throttle limit is more than 7, job execution continues but polling api will be on wait till read/process releases.
Have also tried a simple api which simply returns String instead of polling but that goes on wait too.
Sample of the code as shown below:
#Configuration
#EnableBatchProcessing
public class SpringBatchConfig {
private int core = 200;
#Bean
public Job job() throws Exception {
return jobBuilderFactory.get(SC_Constants.JOB)
.incrementer(new RunIdIncrementer())
.listener(new Listener(transDAO))
.start(step_processRecords()
.build();
}
#Bean
public ThreadPoolTaskExecutor taskExecutor(){
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setCorePoolSize(this.core);
threadPoolTaskExecutor.setMaxPoolSize(this.core);
threadPoolTaskExecutor.setQueueCapacity(this.core);
threadPoolTaskExecutor.setThreadNamePrefix("threadExecutor");
return threadPoolTaskExecutor;
}
#Bean
#StepScope
public JdbcPagingItemReader<Transaction> itemReader(...) {
JdbcPagingItemReader<Transaction> itemReader = new JdbcPagingItemReader<Transaction>();
...
return itemReader;
}
#Bean
#StepScope
public ItemProcessor<Transaction,Transaction> processor() {
return new Processor();
}
#Bean
#StepScope
public ItemWriter<Transaction> writer(...) {
return new Writer();
}
#Bean
public Step step3_processRecords() throws Exception {
return stepBuilderFactory.get(SC_Constants.STEP_3_PROCESS_RECORDS)
.<Transaction,Transaction>chunk(this.chunk)
.reader(itemReader(null,null,null))
.processor(processor())
.writer(writer(null,null,null))
.taskExecutor(taskExecutor())
.throttleLimit(20)
.build();
}
}
file that extends DefaultBatchConfigurer has below:
#Override
public JobLauncher getJobLauncher() {
SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
jobLauncher.setJobRepository(jobRepository);
SimpleAsyncTaskExecutor exec = new SimpleAsyncTaskExecutor();
exec.setConcurrencyLimit(concurrency_limit);
jobLauncher.setTaskExecutor(exec);
return jobLauncher;
}
Edit:
polling api code snippet
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Path("/getJobStatus")
public Response getJobStatus(#RequestBody String body){
JSONObject jsonObject = new JSONObject(body);
Long jobId = jsonObject.get("jobId");
jobExecution = jobExplorer.getJobExecution(jobId);
batchStatus = jobExecution.getStatus().getBatchStatus();
write_count = jobExecution.getStepExecutions().iterator().next().getWriteCount();
responseDto.setJob_id(jobId);
responseDto.setWrite_count(write_count);
responseDto.setStatus(batchStatus.name());
return responseDto;
}
Second edit:
sharing a snippet of the jobrepository setting: using postgres jdbc job repository.
#Component
public class SpringBatchConfigurer extends DefaultBatchConfigurer{
...
#PostConstruct
public void initialize() {
try {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(driverClassName);
dataSource.setUsername(username);
dataSource.setPassword(password);
dataSource.setUrl(dsUrl + "?currentSchema=public");
dataSource.setInitialSize(3);
dataSource.setMinIdle(1);
dataSource.setMaxIdle(3);
dataSource.addConnectionProperty("maxConnLifetimeMillis", "30000");
this.transactionManager = new DataSourceTransactionManager(dataSource);
JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
factory.setDataSource(dataSource);
factory.setTransactionManager(transactionManager);
factory.afterPropertiesSet();
this.jobRepository = factory.getObject();
SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
jobLauncher.setJobRepository(jobRepository);
jobLauncher.afterPropertiesSet();
this.jobLauncher = jobLauncher;
} catch (Exception e) {
throw new BatchConfigurationException(e);
}
}
Third Edit: Tried passing it as a local variable under this step. polling works but now, job execution is not happening. No threads generated. No processing is happening.
#Bean
public Step step3_processRecords() throws Exception {
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setCorePoolSize(this.core_size);
threadPoolTaskExecutor.setMaxPoolSize(this.max_pool_size);
threadPoolTaskExecutor.setQueueCapacity(this.queue_capacity);
threadPoolTaskExecutor.setThreadNamePrefix("threadExecutor");
return stepBuilderFactory.get("step3")
.<Transaction,Transaction>chunk(this.chunk)
.reader(itemReader(null,null,null))
.processor(processor())
.writer(writer(null,null,null))
.taskExecutor(threadPoolTaskExecutor)
.throttleLimit(20)
.build();
}
I'm mocking the jdbc connection, resultset and PreparedStatment.
So, when a run the tests one-by-one works. But if a run all tests from class the method whenSelectB fail.
java.lang.AssertionError: There are 2 rows
Expected: <2>
but: was <0>
at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:20)
at net.sf.jkniv.whinstone.jdbc.dml.MockitoSample.whenSelectB(MockitoSample.java:155)
There is some trick to run this?
public class MockitoSample
{
private DataSource dataSource;
private Connection connection;
private PreparedStatement stmt;
private ResultSet rs;
private ResultSetMetaData rsMetadata;
private DatabaseMetaData dbMetadata;
private RepositoryConfig repositoryConfig;
private SqlContext sqlContext;
private Selectable sql;
#Before
public void setUp() throws SQLException
{
this.connection = mock(Connection.class);
this.dataSource = mock(DataSource.class);
this.stmt = mock(PreparedStatement.class);
this.rs = mock(ResultSet.class);
this.rsMetadata = mock(ResultSetMetaData.class);
this.dbMetadata = mock(DatabaseMetaData.class);
this.repositoryConfig = mock(RepositoryConfig.class);
this.sqlContext = mock(SqlContext.class);
this.sql = mock(Selectable.class);
given(this.dataSource.getConnection()).willReturn(this.connection);
given(this.connection.prepareStatement(anyString(), anyInt(), anyInt())).willReturn(this.stmt);
given(this.stmt.executeQuery()).willReturn(this.rs);
given(this.stmt.executeQuery(anyString())).willReturn(this.rs);
given(this.dbMetadata.getJDBCMajorVersion()).willReturn(1);
given(this.dbMetadata.getJDBCMinorVersion()).willReturn(0);
given(this.dbMetadata.getDriverName()).willReturn("MOCKITO");
given(this.dbMetadata.getDriverVersion()).willReturn("1");
given(this.rs.getMetaData()).willReturn(this.rsMetadata);
given(this.repositoryConfig.getName()).willReturn("Mockito");
given(this.repositoryConfig.lookup()).willReturn(this.dataSource);
given(this.repositoryConfig.getJndiDataSource()).willReturn("jdbc/Mockito");
given(this.repositoryConfig.getProperty(RepositoryProperty.JDBC_ADAPTER_FACTORY.key()))
.willReturn(DataSourceAdapter.class.getName());
given(this.repositoryConfig.getTransactionType()).willReturn(TransactionType.LOCAL);
given(this.repositoryConfig.getQueryNameStrategy()).willReturn("net.sf.jkniv.sqlegance.HashQueryNameStrategy");
given(this.sql.getValidateType()).willReturn(ValidateType.NONE);
given(this.sql.getSql(any())).willReturn("select * from dual");
given(this.sql.getSqlDialect()).willReturn(new AnsiDialect());
given(this.sql.getParamParser()).willReturn(ParamParserFactory.getInstance(ParamMarkType.COLON));
given(this.sql.getStats()).willReturn(NoSqlStats.getInstance());
given(this.sql.getSqlType()).willReturn(SqlType.SELECT);
given(this.sql.asSelectable()).willReturn((Selectable) this.sql);
given(this.sqlContext.getRepositoryConfig()).willReturn(this.repositoryConfig);
given(this.sqlContext.getQuery(anyString())).willReturn(this.sql);
}
#Test
public void whenSelectA() throws SQLException
{
Repository repository = RepositoryService.getInstance().lookup(RepositoryType.JDBC).newInstance(sqlContext);
given(this.rsMetadata.getColumnCount()).willReturn(2);
given(this.rsMetadata.getColumnLabel(1)).willReturn("id");
given(this.rsMetadata.getColumnName(1)).willReturn("id");
given(this.rsMetadata.getColumnLabel(2)).willReturn("name");
given(this.rsMetadata.getColumnName(2)).willReturn("name");
given(this.rs.getMetaData()).willReturn(this.rsMetadata);
given(this.sql.getReturnType()).willReturn(FlatBook.class.getName());
doReturn(FlatBook.class).when(this.sql).getReturnTypeAsClass();
given(rs.next()).willReturn(true, true, false);
given(rs.getObject(1)).willReturn(1001L, 1002L);
given(rs.getObject(2)).willReturn("Beyond Good and Evil", "The Rebel: An Essay on Man in Revolt");
Queryable q = QueryFactory.of("2 FlatBook");
List<FlatBook> books = repository.list(q);
assertThat("There are 2 rows", books.size(), equalTo(2));
assertThat("Row is a FlatBook object", books.get(0), instanceOf(FlatBook.class));
for (FlatBook b : books)
{
assertThat(b.getId(), notNullValue());
assertThat(b.getName(), notNullValue());
}
}
#Test
public void whenSelectB() throws SQLException
{
Repository repository = RepositoryService.getInstance().lookup(RepositoryType.JDBC).newInstance(sqlContext);
given(rsMetadata.getColumnCount()).willReturn(2);
given(this.rsMetadata.getColumnLabel(1)).willReturn("id");
given(this.rsMetadata.getColumnName(1)).willReturn("id");
given(this.rsMetadata.getColumnLabel(2)).willReturn("name");
given(this.rsMetadata.getColumnName(2)).willReturn("name");
given(this.rs.getMetaData()).willReturn(this.rsMetadata);
given(this.sql.getReturnType()).willReturn(FlatAuthor.class.getName());
doReturn(FlatAuthor.class).when(this.sql).getReturnTypeAsClass();
given(rs.next()).willReturn(true, true, false);
given(rs.getObject(1)).willReturn(1L, 2L);
given(rs.getObject(2)).willReturn("Author 1", "Author 2");
Queryable q = QueryFactory.of("2 FlatAuthor");
List<FlatAuthor> books = repository.list(q);
assertThat("There are 2 rows", books.size(), equalTo(2));
assertThat("Row is a FlatAuthor object", books.get(0), instanceOf(FlatAuthor.class));
for (FlatAuthor a : books)
{
assertThat(a.getId(), notNullValue());
assertThat(a.getName(), notNullValue());
}
verify(rs).close();
verify(stmt).close();
verify(connection, atLeast(1)).close();
}
The error happens inside the Repository instance, it uses thers.next ()(ResultSet) method but returnsfalse when it should return true twice.
My Repository instance holds the DataSouce class in theThreadLocal, so when whenSelectB tries to get the new Mock it retrieves the old DataSource that retrieves the old Connection, which gets the old Statement that retrieves the old ResultSet. In other words, I have a dirty context between test. Repository must hold the connection just when a transaction was began.
Thanks #Joakim-Danielson and #Antoniossss
in previous si versions (si 2.11 to be specific and spring 3.1.1) getStandardRequestHeaderNames could be overrided to include Additional Application specific objects in the si message header. Our application relied on this ability (may be wrongfully so) to override this method and supply a custom POJO to be carried downstream consisting of many splitters, aggregators etc. The app used an ws inbound gateway and used the header-mapper attribute to specify the custom soap header mapper.
Any clues on the reasoning behind why getStandardRequestHeaderNames cannot be overriden?
Need some advise on how I can migrate this to the current spring release.
The requirement is to extract elements from soapHeader and map them to an SI message headers as an POJO and send it down stream.
All help appreciated.
Code Snippet: Works with older versions of spring
<int-ws:inbound-gateway id="webservice-inbound-gateway"
request-channel="input-request-channel"
reply-channel="output-response-channel"
header-mapper="CustomSoapHeaderMapper"
marshaller="marshaller"
unmarshaller="marshaller" />
#Component("CustomSoapHeaderMapper")
public class CustomSoapHeaderMapper extends DefaultSoapHeaderMapper {
private static final Logger logger = Logger.getLogger("CustomSoapHeaderMapper");
public static final String HEADER_SEARCH_METADATA = SearchMetadata.HEADER_ATTRIBUTE_NAME;
public static final String HEADER_SERVICE_AUDIT = "XXXXXXXX";
// Use simulation if security token is set to this value
public static final String SECURITY_TOKEN_SIMULATION = "XXXX";
private static final List<String> CUSTOM_HEADER_NAMES = new ArrayList<String>();
static {
CUSTOM_HEADER_NAMES.add(WebServiceHeaders.SOAP_ACTION);
CUSTOM_HEADER_NAMES.add(HEADER_SEARCH_METADATA);
}
private int version =SearchMetadata.VERSION_CURRENT;
public void setVersion(int version) {
this.version = version;
}
#Override
protected List<String> getStandardRequestHeaderNames() {
return CUSTOM_HEADER_NAMES;
}
#Override
protected Map<String, Object> extractUserDefinedHeaders(SoapMessage source) {
// logger.log(Level.INFO,"extractUserDefinedHeaders");
// call base class to extract header
Map<String, Object> map = super.extractUserDefinedHeaders(source);
Document doc = source.getDocument();
SearchMetadata searchMetadata = new SearchMetadata();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
source.writeTo(baos);
baos.flush();
searchMetadata.setRequestXML(baos.toString());
baos.close();
} catch (IOException e1) {
}
//logger.log(Level.WARNING, "Incoming Message " + baos.toString());
SOAPMessage soapMessage = ((SaajSoapMessage) source).getSaajMessage();
// generate TransactionID with UUID value
String transactionID = UUID.randomUUID().toString();
// logger.log(Level.WARNING, "TransactionID=" + transactionID);
Date now = new Date();
searchMetadata.setTransactionID(transactionID);
searchMetadata.setRequestType(SearchMetadata.REQUEST_TYPE_SYNCHRONOUS);
searchMetadata.setRequestTime(now);// initialize the request time
searchMetadata.setReceivedTime(now);// mark time system receives request
searchMetadata.setVersion(version);
Map<String, Object> finalHeaders = new HashMap<String, Object>();
finalHeaders.put(HEADER_SEARCH_METADATA, searchMetadata);
if (!CollectionUtils.isEmpty(map)) {
// copy from other map
finalHeaders.putAll(map);
// check if ServiceAudit is available
SoapHeaderElement serviceAuditElement = null;
for (String key : map.keySet()) {
// logger.log(Level.WARNING, "SoapHeader.{0}", key);
if (StringUtils.contains(key, HEADER_SERVICE_AUDIT)) {
serviceAuditElement = (SoapHeaderElement) map.get(key);
break;
}
}
}
return finalHeaders;
}
// GK Key Thing here for performance improvement is avoiding marshalling
public gov.dhs.ice.ess.schema.ServiceAudit ExtractAuditHeader(Document doc) {
....
}
return serviceAudit;
}
}
Share, please, some code how would you like to see that.
Maybe you can just implement your own SoapHeaderMapper and inject it into WS Inbound Gateway?
You can still reuse your logic and copy/paste the standard behavior from the DefaultSoapHeaderMapper.
UPDATE
The test-case to demonstrate how to add user-defined header manually:
#Test
public void testCustomSoapHeaderMapper() {
DefaultSoapHeaderMapper mapper = new DefaultSoapHeaderMapper() {
#Override
protected Map<String, Object> extractUserDefinedHeaders(SoapMessage source) {
Map<String, Object> headers = super.extractUserDefinedHeaders(source);
headers.put("foo", "bar");
return headers;
}
};
mapper.setRequestHeaderNames("*");
SoapMessage soapMessage = mock(SoapMessage.class);
Map<String, Object> headers = mapper.toHeadersFromRequest(soapMessage);
assertTrue(headers.containsKey("foo"));
assertEquals("bar", headers.get("foo"));
}
I have a Trade class which contains a property currentPrice, which downloads price data from a website using getPricedata() method. The Trade object will show up as a table row in TableView. Now, my task: is to
use the getPricedata() method to grab data from internet, populate the currentPrice cell, whenever the object is created.
relaunch the getPricedata() method to every 1 minute after the object has been created and update table cell.
Below is the basic structure of my code. But I have no idea how to implement this ?
Which package do I need ? Task ? Service ? ScheduledService ?
public class Trade{
private DoubleProperty currentPrice;
// need thread here
public double getPricedata(){
.......
}
}
Use a ScheduledService<Number>, whose Task<Number>'s call() method retrieves and returns the value. Then you can either register an onSucceeded handler with the service, or just bind the Trade's currentPrice to service.lastValue(). Call setPeriod(..) on the service (once) to configure it to run every minute.
Since the currentPrice is being set from the service, you should only expose a ReadOnlyDoubleProperty from your Trade class (otherwise you might try to call currentPriceProperty().set(...) or setCurrentPrice(...), which would fail as it's bound).
I would do something like
public class Trade {
private final ReadOnlyDoubleWrapper currentPrice ;
private final ScheduledService<Number> priceService = new ScheduledService<Number>() {
#Override
public Task<Number> createTask() {
return new Task<Number>() {
#Override
public Number call() {
return getPriceData();
}
};
}
};
public Trade() {
priceService.setPeriod(Duration.minutes(1));
// in case of errors running service:
priceService.setOnFailed(e -> priceService.getException().printStackTrace());
currentPrice = new ReadOnlyDoubleWrapper(0);
currentPrice.bind(priceService.lastValueProperty());
startMonitoring();
}
public final void startMonitoring() {
priceService.restart();
}
public final void stopMonitoring() {
priceService.cancel();
}
public ReadOnlyDoubleProperty currentPriceProperty() {
return currentPrice.getReadOnlyProperty();
}
public final double getCurrentPrice() {
return currentPriceProperty().get();
}
private double getPriceData() {
// do actual retrieval work here...
}
}
(Code just typed in here without testing, but it should give you the idea.)
I have a service that needs to invoke a runnable class.
Here are the lines of code that are being used in my service.
#Autowired
private LinkBrc2MemberProfile brcTask;
// Background Task.
SimpleAsyncTaskExecutor sate = new SimpleAsyncTaskExecutor();
sate.createThread(new LinkBrc2MemberProfile(user));
Here is my Runnable class
#Service
public class LinkBrc2MemberProfile implements Runnable {
private final Logger log = LoggerFactory.getLogger(LinkBrc2MemberProfile.class);
#Autowired
private LoyaltyDao dao;
private Member member;
public LinkBrc2MemberProfile() {
super();
}
public LinkBrc2MemberProfile(Member member) {
this.member = member;
}
public void run() {
log.debug("*** Member User Name: " + member.getString("USER_NAME"));
String emailAddress = member.getString("USER_NAME");
Map<String, Object> map = dao.findBrcByEmailAddress( emailAddress );
log.debug("==========================================================");
if( ! map.isEmpty() ) {
try {
//a.CUSTOMER_ID, a.EMAIL_ADDRESS, b.card_no
String customerId = (String) map.get("CUSTOMER_ID");
String brcCardNumber = (String) map.get("CARD_NO");
log.debug("\ncustomerId: " + customerId + " brcCardNumber: " + brcCardNumber);
if(!brcCardNumber.equals("")) {
// Add the Be Rewarded Card.
HashMap<String, String> userAttributes = new HashMap<String, String>();
String brcNumber = member.getString("BREWARDED_CARD_NO");
if (brcNumber.equals("")) {
userAttributes.put("BREWARDED_CARD_NO", brcCardNumber);
try {
member.putAll(userAttributes);
} catch (Exception e) {
String errorMessage = "Unable to save user's BRC information due to: " + e.getMessage();
log.error("{}", errorMessage);
}
}
}
} catch (Exception e) {
log.error(e.getMessage());
e.printStackTrace();
}
}
}
I'm not seeing any errors in the log but at the same time it does not appear to be invoking the Runnable class. Am I missing an annotation somewhere? Are there any good examples that you can point me to, the only ones I have found use XML files to configure the runnable class I would like to use annotations. Thanks in Advance.
I've updated my service to do the following.
Please help, my DAO is NULL so it looks like my #Autowired in my Runnable class is not wiring it in.
I've added the following bean to my bean-config.xml file.
<bean id="brcType" class="com.ws.ocp.service.LinkBrc2MemberProfile" scope="prototype"/>
I removed my #Autowired annotation and added the following to my service class.
ClassPathResource rsrc = new ClassPathResource("bean-config.xml");
XmlBeanFactory factory = new XmlBeanFactory(rsrc);
LinkBrc2MemberProfile brcTask = (LinkBrc2MemberProfile) factory.getBean("brcType");
SimpleAsyncTaskExecutor sate = new SimpleAsyncTaskExecutor();
// Set Member attribute
brcTask.setMember(user);
// Executer
sate.execute(brcTask);
Why is my dao still null?
The runnable will throw a NullPointerException, since you create it yourself (using the new operator), instead of letting Spring create it. This obviously means that the autowired DAO attribute won't be autowired, which will lead to a NPE when calling dao.findBrcByEmailAddress(...).
You should get your Runnable instance from the bean factory (as a prototype), set its member attribute, and then submit it to the executor.
To answer your question of how to properly use a Prototype-Bean, this is my favorite way:
#Component
abstract class MyBean {
/* Factory method that will be installed by Spring */
#Lookup
protected abstract YourPrototypeBean createBean();
void someCode() {
YourPrototypeBean bean = createBean();
}
}
Since it's a factory method, you can create as many instances as you like.