Mockito running independently works but fail when run together - mockito

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

Related

Mockito (How to correctly mock nested objects)

I have this next class:
#Service
public class BusinessService {
#Autowired
private RedisService redisService;
private void count() {
String redisKey = "MyKey";
AtomicInteger counter = null;
if (!redisService.isExist(redisKey))
counter = new AtomicInteger(0);
else
counter = redisService.get(redisKey, AtomicInteger.class);
try {
counter.incrementAndGet();
redisService.set(redisKey, counter, false);
logger.info(String.format("Counter incremented by one. Current counter = %s", counter.get()));
} catch (JsonProcessingException e) {
logger.severe(String.format("Failed to increment counter."));
}
}
// Remaining code
}
and this this my RedisService.java class
#Service
public class RedisService {
private Logger logger = LoggerFactory.getLogger(RedisService.class);
#Autowired
private RedisConfig redisConfig;
#PostConstruct
public void postConstruct() {
try {
String redisURL = redisConfig.getUrl();
logger.info("Connecting to Redis at " + redisURL);
syncCommands = RedisClient.create(redisURL).connect().sync();
} catch (Exception e) {
logger.error("Exception connecting to Redis: " + e.getMessage(), e);
}
}
public boolean isExist(String redisKey) {
return syncCommands.exists(new String[] { redisKey }) == 1 ? true : false;
}
public <T extends Serializable> void set(String key, T object, boolean convertObjectToJson) throws JsonProcessingException {
if (convertObjectToJson)
syncCommands.set(key, writeValueAsString(object));
else
syncCommands.set(key, String.valueOf(object));
}
// Remaining code
}
and this is my test class
#Mock
private RedisService redisService;
#Spy
#InjectMocks
BusinessService businessService = new BusinessService();
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
#Test
public void myTest() throws Exception {
for (int i = 0; i < 50; i++)
Whitebox.invokeMethod(businessService, "count");
// Remaining code
}
my problem is the counter always equals to one in logs when running tests
Counter incremented by one. Current counter = 1(printed 50 times)
and it should print:
Counter incremented by one. Current counter = 1
Counter incremented by one. Current counter = 2
...
...
Counter incremented by one. Current counter = 50
this means the Redis mock always passed as a new instance to BusinessService in each method call inside each loop, so how I can force this behavior to become only one instance used always for Redis inside the test method ??
Note: Above code is just a sample to explain my problem, but it's not a complete code.
Your conclusion that a new RedisService is somehow created in each iteration is wrong.
The problem is that it is a mock object for which you haven’t set any behaviours, so it responds with default values for each method call (null for objects, false for bools, 0 for ints etc).
You need to use Mockito.when to set behaviour on your mocks.
There is some additional complexity caused by the fact that:
you run the loop multiple times, and behaviour of the mocks differ between first and subsequent iterations
you create cached object in method under test. I used doAnswer to capture it.
You need to use doAnswer().when() instead of when().thenAnswer as set method returns void
and finally, atomicInt variable is modified from within the lambda. I made it a field of the class.
As the atomicInt is modified each time, I again used thenAnswer instead of thenReturn for get method.
class BusinessServiceTest {
#Mock
private RedisService redisService;
#InjectMocks
BusinessService businessService = new BusinessService();
AtomicInteger atomicInt = null;
#BeforeEach
public void setup() {
MockitoAnnotations.initMocks(this);
}
#Test
public void myTest() throws Exception {
// given
Mockito.when(redisService.isExist("MyKey"))
.thenReturn(false)
.thenReturn(true);
Mockito.doAnswer((Answer<Void>) invocation -> {
atomicInt = invocation.getArgument(1);
return null;
}).when(redisService).set(eq("MyKey"), any(AtomicInteger.class), eq(false));
Mockito.when(redisService.get("MyKey", AtomicInteger.class))
.thenAnswer(invocation -> atomicInt);
// when
for (int i = 0; i < 50; i++) {
Whitebox.invokeMethod(businessService, "count");
}
// Remaining code
}
}
Having said that, I still find your code questionable.
You store AtomicInteger in Redis cache (by serializing it to String). This class is designed to be used by multiple threads in a process, and the threads using it the same counter need to share the same instance. By serializing it and deserializing on get, you are getting multiple instances of the (conceptually) same counter, which, to my eyes, looks like a bug.
smaller issue: You shouldn't normally test private methods
2 small ones: there is no need to instantiate the field annotated with #InjectMocks. You don't need #Spy as well.

Spark Cassandra Connector Java API append/remove data in a collection fail

I am trying to append values to a column of type set, via the JAVA API.
It seems that the connector disregards the type of CollectionBehavior I am setting,
and always overrides the previous collection.
Even when I use CollectionRemove, the value to be removed is added to the collection.
I am following the example as shown in:
https://datastax-oss.atlassian.net/browse/SPARKC-340?page=com.atlassian.jira.plugin.system.issuetabpanels%3Achangehistory-tabpanel
I am using:
spark-core_2.11 2.2.0
spark-cassandra-connector_2.11 2.0.5
Cassandra 2.1.17
Could it be that this feature is no supported on those versions?
Here is the implementation code:
// CASSANDRA TABLE
CREATE TABLE test.profile (
id text PRIMARY KEY,
dates set<bigint>,
)
// ENTITY
public class ProfileRow {
public static final Map<String, String> namesMap;
static {
namesMap = new HashMap<>();
namesMap.put("id", "id");
namesMap.put("dates", "dates");
}
private String id;
private Set<Long> dates;
public ProfileRow() {}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Set<Long> getDates() {
return dates;
}
public void setDates(Set<Long> dates) {
this.dates = dates;
}
}
public void execute(JavaSparkContext context) {
List<ProfileRow> elements = new LinkedList<>();
ProfileRow profile = new ProfileRow();
profile.setId("fGxTObQIXM");
Set<Long> dates = new HashSet<>();
dates.add(1l);
profile.setDates(dates);
elements.add(profile);
JavaRDD<ProfileRow> rdd = context.parallelize(elements);
RDDAndDStreamCommonJavaFunctions<T>.WriterBuilder wb = javaFunctions(rdd)
.writerBuilder("test", "profile", mapToRow(ProfileRow.class, ProfileRow.namesMap));
CollectionColumnName appendColumn = new CollectionColumnName("dates", Option.empty(), CollectionAppend$.MODULE$);
scala.collection.Seq<ColumnRef> columnRefSeq = JavaApiHelper.toScalaSeq(Arrays.asList(appendColumn));
SomeColumns columnSelector = SomeColumns$.MODULE$.apply(columnRefSeq);
wb.withColumnSelector(columnSelector);
wb.saveToCassandra();
}
Thanks,
Shai
I found the answer. There are 2 things I had to change:
Add the primary key column to the column selector.
WriterBuilder.withColumnSelector() generates a new instance of WriterBuilder, so I had to store the new instance.
:
RDDAndDStreamCommonJavaFunctions<T>.WriterBuilder wb = javaFunctions(rdd)
.writerBuilder("test", "profile", mapToRow(ProfileRow.class, ProfileRow.namesMap));
ColumnName pkColumn = new ColumnName("id", Option.empty())
CollectionColumnName appendColumn = new CollectionColumnName("dates", Option.empty(), CollectionAppend$.MODULE$);
scala.collection.Seq<ColumnRef> columnRefSeq = JavaApiHelper.toScalaSeq(Arrays.asList(pkColumn, appendColumn));
SomeColumns columnSelector = SomeColumns$.MODULE$.apply(columnRefSeq);
wb = wb.withColumnSelector(columnSelector);
wb.saveToCassandra();

AdviceAdapter onMethodExit never called

I'm attempting to instrument a cassandra driver and in particular need to modify a ResultSet class to hang on to some information. In order to do this I need to modify the code where the instance is being allocated, which is a static method in another class. The code has this snippet in it:
return r.metadata.pagingState == null
? new SinglePage(columnDefs, tokenFactory, protocolVersion, columnDefs.codecRegistry, r.data, info)
: new MultiPage(columnDefs, tokenFactory, protocolVersion, columnDefs.codecRegistry, r.data, info, r.metadata.pagingState, session);
It also has other returns within the method. So my thought was to use an AdviceAdapter on this method and use the onMethodExit(). However, my method was never called. That seems absurd since.. the method has to be returning! After a little debugging, I find the visitInsn() in the AdviceAdapter class is being called just once, with an opcode of IALOAD (load an int from an array?).
I guess my question is.. what the hell is going on? Heh.. sorry, bonked my head on my desk a few too many times today.
EDIT: I changed my class to be a simple MethodVisitor just to see if could see more opcodes, and indeed I do! I see it all! I just no longer have access to dup(). :(
I have used an EmailAdviceAdaptor (for javax/mail/Transport) in one of my project, Code is given below. Hope this code will help to resolve your issue.
package com.mail.agent.adapter;
import com.mail.jtm.BTMConstants;
import com.mail.org.objectweb.asm.Label;
import com.mail.org.objectweb.asm.MethodVisitor;
import com.mail.org.objectweb.asm.Opcodes;
import com.mail.org.objectweb.asm.Type;
import com.mail.org.objectweb.asm.commons.AdviceAdapter;
public class MyEmailAdviceAdapter extends AdviceAdapter {
private String methodName;
private String className;
private String description;
private static final String MAIL_SEND_METHOD1_DESC="(Ljavax/mail/Message;)V";
private static final String MAIL_SEND_METHOD2_DESC="(Ljavax/mail/Message;[Ljavax/mail/Address;)V";
private static final String MAIL_SENDMESSAGE_METHOD_DESC="(Ljavax/mail/Message;[Ljavax/mail/Address;)V";
private boolean isSendMethod;
private int okFlag = newLocal(Type.BOOLEAN_TYPE);
Label startFinally = new Label();
public MyEmailAdviceAdapter(int access , MethodVisitor mv , String methodName, String description, String className, int classFileVersion){
super(Opcodes.ASM5 , mv, access, methodName, description);
this.className = className;
this.methodName = methodName;
this.description = description;
this.isSendMethod = false;
if(methodName.equals("send")){
if( description.equals(MAIL_SEND_METHOD1_DESC) || description.equals(MAIL_SEND_METHOD2_DESC)){
isSendMethod = true;
}
}
else if(methodName.equals("sendMessage") && description.equals(MAIL_SENDMESSAGE_METHOD_DESC)){
isSendMethod = true;
}
}
public void visitCode() {
super.visitCode();
mv.visitLabel(startFinally);
}
protected void onMethodEnter(){
if(isSendMethod) {
mv.visitInsn(Opcodes.ICONST_0);
mv.visitVarInsn(ISTORE, okFlag);
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitLdcInsn(className);
mv.visitLdcInsn(methodName);
mv.visitLdcInsn(description);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "com/mail/agent/trace/MailTracer", "mailMethodBegin", "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z"; , false);
mv.visitVarInsn(ISTORE, okFlag);
}
}
protected void onMethodExit(int opcode){
if(opcode!=ATHROW) {
onFinally(opcode);
}
}
public void visitMaxs(int maxStack, int maxLocals){
Label endFinally = new Label();
mv.visitTryCatchBlock(startFinally, endFinally, endFinally, null);
mv.visitLabel(endFinally);
onFinally(ATHROW);
mv.visitInsn(ATHROW);
mv.visitMaxs(maxStack, maxLocals);
}
private void onFinally(int opcode){
if(isSendMethod){
// If the method throws any exception
if(opcode == ATHROW){
mv.visitInsn(Opcodes.DUP);
mv.visitLdcInsn(className);
mv.visitLdcInsn(methodName);
mv.visitLdcInsn(description);
mv.visitVarInsn(ILOAD, okFlag);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "com/mail/agent/trace/MailTracer", "recordException", "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)V", false);
}
mv.visitLdcInsn(className);
mv.visitLdcInsn(methodName);
mv.visitLdcInsn(description);
mv.visitVarInsn(ILOAD, okFlag);
mv.visitLdcInsn(opcode);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "com/mail/agent/trace/MailTracer", "mailMethodEnd", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZI)V", false);
}
}
}

CassandraOperations queryForObject() method always returning PrimaryKey instead of Entity Object

I am trying to access Cassandra # localhost using a standalone main() method. The main() method uses DataStax driver and CassandraOperations class from spring-data-cassandra module. CassandraOperation's queryForObject() method always return the primary key instead of Entity Object.
I am just using the code example given in the Spring Data Documentation.
Apache-Cassandra version : 2.1.2
Spring-Data-Cassandra version : 1.2.0.RELEASE
Entity Class :
import org.springframework.data.cassandra.mapping.PrimaryKey;
import org.springframework.data.cassandra.mapping.Table;
#Table
public class Person {
#PrimaryKey
private String id;
private String name;
private int age;
public Person(String id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
#Override
public String toString() {
return "Person [id=" + id + ", name=" + name + ", age=" + age + "]";
}
}
Client Code:
public class CassandraApp {
private static final Logger LOG = LoggerFactory.getLogger(CassandraApp.class);
private static Cluster cluster;
private static Session session;
public static void main(String[] args) {
try {
cluster = Cluster.builder().addContactPoints(InetAddress.getLocalHost()).build();
session = cluster.connect("person");
CassandraOperations cassandraOps = new CassandraTemplate(session);
cassandraOps.insert(new Person("1234567890", "David", 40));
Select s = QueryBuilder.select().from("person");
s.where(QueryBuilder.eq("id", "1234567890"));
System.out.println(cassandraOps.queryForObject(s, Person.class).getId());
cassandraOps.truncate("person");
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
}
Runtime exception:
Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to com.prashanth.ts.entity.Person
at com.prashanth.ts.client.CassandraApp.main(CassandraApp.java:40)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)
I am new to Spring Data. Any one can help me identify what I am doing wrong here.
I also tried removing the QueryBuilder and passing a simple query String like "select * from person" to the queryForObject() methodd
Note :
The insert operation is working perfectly.
I was able to make it work using selectOne method instead of queryForObject.
LOG.info(cassandraOps.selectOne(s, Person.class).getId());
Judging by documentation here, you need to add one more method(all) for QueryBuilder:
Select s = QueryBuilder.select().all().from("person");
On a side note, you are using spring-data-cassandra but you are not utilizing it's best features which would make your code much simpler.
I can see where the OP could have gotten confused here. Finding no code extension provided by Eclipse I went to the org.springframework.data.cassandra.core
Interface CassandraOperations documentation. There is no queryForObject documented so unless someone can explain otherwise
LOG.info(cassandraOps.queryForObject(s, Person.class).getId());
is just bad code. I tried to find the correct usage of queryForObject but all the searches took me back to the example in question which seems to have originated in 2008. Who knows, at one point it may have worked. The OP was trying to use Cassandra Operations to extract information from "s". I liked the idea of Amit T and got something working. I had my own class using Company instead of Person but the idea is the same.
try {
cluster = Cluster.builder().withoutMetrics().addContactPoints(InetAddress.getByName("192.168.1.5") ).build();
session = cluster.connect("rant");
CassandraOperations cassandraOps = new CassandraTemplate(session);
cassandraOps.insert(new Companies("name1", "user", "category", "first", "last", "city", "state", "zipcode", "phone", "email",
"addr1c", "adddr2c", "cityc", "statec", "zipcodec", "phonec", "emailc", "website", 0.0, 0.0,
0, 0, "pr", 0, "text"));
Select s = QueryBuilder.select().from("companies");
s.where(QueryBuilder.eq("company_company", "name1"));
// LOG.info(cassandraOps.queryForObject(s, Companies.class).getId());
LOG.info(cassandraOps.selectOne(s, Companies.class).CompanyInformation());
cassandraOps.truncate(Companies.class); // empties the table
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
I also created my own CompanyInformation() just as an exercise.
public String CompanyInformation() {
System.out.println("Company Information " + this.company_company);
return this.company_userid;
}
The output was as expected.
19:35:24.456 [cluster1-nio-worker-2] DEBUG com.datastax.driver.core.Connection - Connection[/192.168.1.5:9042-2, inFlight=1, closed=false] Keyspace set to rant
Company Information name1 <== from CompanyInformation()
19:35:24.483 [main] INFO com.androidcommand.app.SpringRbsApplication - user <== from LOG.Info
19:35:24.485 [main] DEBUG org.springframework.data.cassandra.core.cql.CqlTemplate - Executing CQL Statement [TRUNCATE companies;]

Multithreaded Hibernate Session

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.

Resources