Initialize class with static method not managed by Spring - java

I have a class called ConfigManagement which uses only static methods/fields. One of the static methods, called initializeConfig() takes a Property object (points at application.properties) as input and populates the fields and calls some other methods with the values from the application.properties file.
public class ConfigManagement {
private static String signatureAlgorithm;
private static String myName;
private static RSAPublicKey myPublicKey;
private static RSAPrivateKey myPrivateKey;
private static HashMap<String, RSAPublicKey> peerPubKeys = new HashMap<String, RSAPublicKey>();
private static boolean isInitialized = false;
/**
* #return the signatureAlgorithm
*/
public static void initializeConfig(Properties props) {
signatureAlgorithm = props.getProperty("cybertrust.crypto.signatureAlgorithm");
myName = props.getProperty("cybertrust.crypto.myName");
try {
try {
myPublicKey = Loader.getPublicKeyFromCertificateFile(props.getProperty("cybertrust.crypto.myCertificate"));
}
catch (Exception e) {
throw new IllegalStateException("cybertrust.crypto.myCertificate is empty, the file is not found or it contains invalid data");
}
try {
myPrivateKey = Loader.getPrivateKeyFromFile(props.getProperty("cybertrust.crypto.myPrivateKey"));
}
catch (Exception e) {
throw new IllegalStateException("cybertrust.crypto.myPrivateKey is empty, the file is not found or it contains invalid data");
}
peerPubKeys.put(myName, myPublicKey);
int peerCounter = 0;
do {
String peerNameProp = String.format("cybertrust.crypto.peerModules.%d.name", peerCounter);
String peerName = props.getProperty(peerNameProp);
if (peerName == null)
break;
String peerNameCertFileProp = String.format("cybertrust.crypto.peerModules.%d.certificate", peerCounter);
String peerNameCertFile = props.getProperty(peerNameCertFileProp);
if (peerNameCertFile == null) // Do not halt the program, produce though an error
Logger.getLogger("ConfigManagement").log(Level.SEVERE,
String.format("Property %s not found while property %s is defined", peerNameCertFile, peerNameProp));
// instantiate public key from file
try {
RSAPublicKey peerRsaPubKey = Loader.getPublicKeyFromCertificateFile(peerNameCertFile);
peerPubKeys.put(peerName, peerRsaPubKey);
}
catch (Exception e) {
Logger.getLogger("ConfigManagement").log(Level.SEVERE,
String.format("File %s specified in property %s not found or does not contains a valid RSA key", peerNameCertFile, peerNameCertFileProp)); }
peerCounter++;
} while (true);
}
catch (Exception e) {
throw(e);
}
if ((myPublicKey == null) || (signatureAlgorithm == null) || (myName == null))
throw new IllegalStateException("one of the properties cybertrust.crypto.signatureAlgorithm, cybertrust.crypto.myName, cybertrust.crypto.myPublicKey, cybertrust.crypto.myPrivateKey is not defined");
isInitialized = true;
}
private static void testInitialized() {
if (!isInitialized)
throw new IllegalStateException("The configuration has not been initialized");
}
public static String getSignatureAlgorithm() {
testInitialized();
return signatureAlgorithm;
}
/**
* #return the myName
*/
public static String getMyName() {
testInitialized();
return myName;
}
/**
* #return the myPublicKey
*/
public static RSAPublicKey getMyPublicKey() {
testInitialized();
return myPublicKey;
}
/**
* #return the myPrivateKey
*/
public static RSAPrivateKey getMyPrivateKey() {
testInitialized();
return myPrivateKey;
}
public static RSAPublicKey getPublicKey(String peerName) throws NoSuchElementException {
testInitialized();
RSAPublicKey result = peerPubKeys.get(peerName);
if (result == null)
throw new NoSuchElementException("No known key for module " + peerName);
else
return result;
}
}
The application.properties file looks something like this:
cybertrust.crypto.myName=tms1235.cybertrust.eu
cybertrust.crypto.myCertificate=tms1235.cert.pem
cybertrust.crypto.myPrivateKey=tms1235.key.pem
cybertrust.crypto.signatureAlgorithm=SHA256withRSA
cybertrust.crypto.peerModules.0.name=sga1234.cybertrust.eu
cybertrust.crypto.peerModules.0.certificate=sga1234.cert.pem
cybertrust.crypto.peerModules.1.name=tms1234.cybertrust.eu
cybertrust.crypto.peerModules.1.certificate=tms1234.cert.pem
In a simple Java project I run ConfigManagement.initializeConfig(props); in main() and the fields are initialized and I can use the rest of the methods. In Spring it's not that simple.
I am trying to integrate this code in a SpringBoot application and I don't know how/where to initialize this class.
I am posting the Spring configuration for reference:
#Configuration
#EnableWebMvc
#EnableTransactionManagement
#ComponentScan("com.cybertrust.tms")
//#PropertySource({ "classpath:persistence-mysql.properties" })
#PropertySource({ "classpath:model.properties" })
public class DemoAppConfig implements WebMvcConfigurer {
#Autowired
private Environment env;
private Logger logger = Logger.getLogger(getClass().getName());
// define a bean for ViewResolver
#Bean
public DataSource myDataSource() {
// create connection pool
ComboPooledDataSource myDataSource = new ComboPooledDataSource();
// set the jdbc driver
try {
myDataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
}
catch (PropertyVetoException exc) {
throw new RuntimeException(exc);
}
// for sanity's sake, let's log url and user ... just to make sure we are reading the data
logger.info("jdbc.url=" + env.getProperty("spring.datasource.url"));
logger.info("jdbc.user=" + env.getProperty("spring.datasource.username"));
// set database connection props
myDataSource.setJdbcUrl(env.getProperty("spring.datasource.url"));
myDataSource.setUser(env.getProperty("spring.datasource.username"));
myDataSource.setPassword(env.getProperty("spring.datasource.password"));
// set connection pool props
myDataSource.setInitialPoolSize(getIntProperty("connection.pool.initialPoolSize"));
myDataSource.setMinPoolSize(getIntProperty("connection.pool.minPoolSize"));
myDataSource.setMaxPoolSize(getIntProperty("connection.pool.maxPoolSize"));
myDataSource.setMaxIdleTime(getIntProperty("connection.pool.maxIdleTime"));
return myDataSource;
}
private Properties getHibernateProperties() {
// set hibernate properties
Properties props = new Properties();
props.setProperty("hibernate.dialect", env.getProperty("hibernate.dialect"));
props.setProperty("hibernate.show_sql", env.getProperty("hibernate.show_sql"));
props.setProperty("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto"));
return props;
}
// need a helper method
// read environment property and convert to int
private int getIntProperty(String propName) {
String propVal = env.getProperty(propName);
// now convert to int
int intPropVal = Integer.parseInt(propVal);
return intPropVal;
}
#Bean
public LocalSessionFactoryBean sessionFactory(){
// create session factorys
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
// set the properties
sessionFactory.setDataSource(myDataSource());
sessionFactory.setPackagesToScan(env.getProperty("hibernate.packagesToScan"));
sessionFactory.setHibernateProperties(getHibernateProperties());
return sessionFactory;
}
#Bean
#Autowired
public HibernateTransactionManager transactionManager(SessionFactory sessionFactory) {
// setup transaction manager based on session factory
HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setSessionFactory(sessionFactory);
return txManager;
}
#Bean
public ModelMapper modelMapper() {
return new ModelMapper();
}
#Bean
public ConfigManagement configManagement() {
return new ConfigManagement();
}
}
And the Spring Boot main():
#Configuration
#EnableWebMvc
#EnableTransactionManagement
#ComponentScan("com.cybertrust.tms")
//#PropertySource({ "classpath:persistence-mysql.properties" })
#PropertySource({ "classpath:model.properties" })
//#EnableAutoConfiguration(exclude = HibernateJpaAutoConfiguration.class)
#SpringBootApplication(exclude = {HibernateJpaAutoConfiguration.class})
public class TMS extends SpringBootServletInitializer {
public static void main(String[] args) throws Exception {
SpringApplication.run(TMS.class, args);
}
}

Your static solution won't work in a Spring environment as is, because static can be executed before Spring is up and loaded all beans and properties
You should rewrite your code in a Spring way by getting propertyies using #Value
Injecting a property with the #Value annotation is straightforward:
#Value( "${jdbc.url}" )
private String jdbcUrl;

In order to integrate this code to a Spring project, I had to:
Make the class a bean, managed by Spring, by adding it in my configuration file that I posted in my question, I added this:
#Bean
public ConfigManagement configManagement() {
return new ConfigManagement();
}
Remove the static declaration from the class properties and use the #Value annotation to initialize them from the application.properties file, as suggested by #user7294900.
However, some of the class properties were not primitive types and couldn't be initialized directly from the application.properties. They needed some "business-logic" to be run at initialization time. In order to achieve that, I had to remove the static declaration and add the #PostConstruct annotation in the initializeConfig() method, which is the one that handled the initialization of the rest of the properties.
public class ConfigManagement {
#Value("${cybertrust.crypto.signatureAlgorithm}")
private String signatureAlgorithm;
#Value("${cybertrust.crypto.myName}")
private String myName;
#Value("${cybertrust.crypto.myCertificate}")
private String myCertificate;
#Value("${cybertrust.crypto.myPrivateKey}")
private String myPrivateKey;
private RSAPublicKey myRSAPublicKey;
private RSAPrivateKey myRSAPrivateKey;
private HashMap<String, RSAPublicKey> peerPubKeys = new HashMap<String, RSAPublicKey>();
private boolean isInitialized = false;
int peerCounter;
/**
* #return the signatureAlgorithm
*/
public ConfigManagement() {
}
#PostConstruct
public void initializeConfig() throws Exception {
try {
try {
myRSAPublicKey = Loader.getPublicKeyFromCertificateFile("C:\\Users\\Findorgri\\git\\trust-management\\TMS-rest\\" + myCertificate);
}
catch (Exception e) {
throw new IllegalStateException("cybertrust.crypto.myCertificate is empty, the file is not found or it contains invalid data");
}
try {
myRSAPrivateKey = Loader.getPrivateKeyFromFile("C:\\Users\\Findorgri\\git\\trust-management\\TMS-rest\\" + myPrivateKey);
}
catch (Exception e) {
throw new IllegalStateException("cybertrust.crypto.myPrivateKey is empty, the file is not found or it contains invalid data");
}
peerPubKeys.put(myName, myRSAPublicKey);
Properties props = loadProperties("C:\\Users\\Findorgri\\git\\trust-management\\TMS-rest\\src\\main\\resources\\application.properties");
if (props == null) {
throw new Exception("Properties file not found");
}
peerCounter = 0;
do {
String peerNameProp = String.format("cybertrust.crypto.peerModules.%d.name", peerCounter);
String peerName = props.getProperty(peerNameProp);
System.out.println("####TEST####\n" + peerNameProp + "\n" + peerName +"\n####TEST####");
if (peerName == null)
break;
String peerNameCertFileProp = String.format("cybertrust.crypto.peerModules.%d.certificate", peerCounter);
String peerNameCertFile = props.getProperty(peerNameCertFileProp);
System.out.println("####TEST####\n" + peerNameCertFileProp + "\n" + peerNameCertFile +"\n####TEST####");
if (peerNameCertFile == null) // Do not halt the program, produce though an error
Logger.getLogger("ConfigManagement").log(Level.SEVERE,
String.format("Property %s not found while property %s is defined", peerNameCertFile, peerNameProp));
// instantiate public key from file
try {
RSAPublicKey peerRsaPubKey = Loader.getPublicKeyFromCertificateFile("C:\\Users\\Findorgri\\git\\trust-management\\TMS-rest\\" + peerNameCertFile);
peerPubKeys.put(peerName, peerRsaPubKey);
}
catch (Exception e) {
Logger.getLogger("ConfigManagement").log(Level.SEVERE,
String.format("File %s specified in property %s not found or does not contains a valid RSA key", peerNameCertFile, peerNameCertFileProp)); }
peerCounter++;
} while (true);
}
catch (Exception e) {
throw(e);
}
if ((myRSAPublicKey == null) || (signatureAlgorithm == null) || (myName == null))
throw new IllegalStateException("one of the properties cybertrust.crypto.signatureAlgorithm, cybertrust.crypto.myName, cybertrust.crypto.myPublicKey, cybertrust.crypto.myPrivateKey is not defined");
isInitialized = true;
peerPubKeys.forEach((key, value) -> System.out.println(key + ":" + value));
}
....
Finally, for completeness' sake, for the initializeConfig() method to have access at the application.properties I had to use this method:
private static Properties loadProperties(String fileName) throws IOException {
FileInputStream fis = null;
Properties prop = null;
try {
fis = new FileInputStream(fileName);
prop = new Properties();
prop.load(fis);
} catch(FileNotFoundException fnfe) {
fnfe.printStackTrace();
} catch(IOException ioe) {
ioe.printStackTrace();
} finally {
fis.close();
}
return prop;
}

Related

How to transfer a list of strings from a file to property of application.properties (using Spring boot 2.3.x)

I have a application.yaml
app:
list: /list.txt
list.txt
Also I have a file with list of strings. It locates into /resources(in the root /resource).
first
second
third
class
public class Bean{
#Value("${app.list}")
private List<String> listProp = new ArrayList<>();
public void print(){
System.out.println(listProp);
}
}
I have found that:
public class ResourceReader {
public static String asString(Resource resource) {
try (Reader reader = new InputStreamReader(resource.getInputStream(), UTF_8)) {
return FileCopyUtils.copyToString(reader);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
public static String readFileToString(String path) {
ResourceLoader resourceLoader = new DefaultResourceLoader();
Resource resource = resourceLoader.getResource(path);
return asString(resource);
}
}
#Configuration
public class ConfigurationResource {
#Configuration
public class ConfigurationResource {
#Value("${app.list}")
private String pathToFile;
#Bean
public List<String> resourceString() {
String blackList = ResourceReader.readFileToString(pathToFile);
return List.of(blackList.split("\n"));
}
}
}
#RequiredArgsConstructor
public class HelloController {
private final List<String> resourceString;
...
}
This is necessary in order not to manually write a list of strings to the property app.name (there are several hundred lines).
However, I find it difficult to figure out how to do it at low cost. So that it can be easily maintained.
maybe there is an easier way ? I would not like to add a hardcoding value in the configuration class
Maybe someone has some ideas ?
Here is the solution from my understanding if you have to keep lines in the text file that you've shared:
public class Bean {
#Value("${app.list}")
private String listProp; // only get name of file
public void print(){
ClassLoader classLoader = getClass().getClassLoader();
InputStream is = classLoader.getResourceAsStream(listProp);
StringBuilder sb = new StringBuilder();
try {
for (int ch; (ch = is.read()) != -1; ) {
sb.append((char) ch);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
System.out.println(sb);
}
}

Return List of Objects from Spring Batch Custom ItemReader

I have a requirement to process thousands of Payments..So I used VtdXml instead of StaxEventItemReader in Spring Batch, I have created Custom Item Reader for the same. In order to read huge xml with Multi Threading, I have created partition with 10 Threads. I am splitting huge xml file into 10 files and assigning to each Thread in Partition. Once I read the xml and I will convert into list of Objects and send to Writer. After received in Writer, I will customize the List of Objects and Merge into the final List. Whenever Am returning list of Objects read called again and it is never ending. How do I pass the list of Objects to the Writer and merge into the final List?
public class VtdWholeItemReader<T> implements ResourceAwareItemReaderItemStream<T> {
private Resource resource;
private boolean noInput;
private boolean strict = true;
private InputStream inputStream;
private int index = 0;
#Override
public void open(ExecutionContext executionContext) {
Assert.notNull(resource, "The Resource must not be null.");
noInput = true;
if (!resource.exists()) {
if (strict) {
throw new IllegalStateException("Input resource must exist (reader is in 'strict' mode)");
}
log.warn("Input resource does not exist " + resource.getDescription());
return;
}
if (!resource.isReadable()) {
if (strict) {
throw new IllegalStateException("Input resource must be readable (reader is in 'strict' mode)");
}
log.warn("Input resource is not readable " + resource.getDescription());
return;
}
noInput = false;
}
#Override
public void update(ExecutionContext executionContext) {
}
#Override
public void close() {
try {
if (inputStream != null) {
inputStream.close();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
inputStream = null;
}
}
#Override
public void setResource(Resource resource) {
this.resource = resource;
}
#Override
public T read()
throws java.lang.Exception, UnexpectedInputException, ParseException, NonTransientResourceException {
if (noInput) {
return null;
}
List<Payment> paymentList = new ArrayList<Payment>();
try {
VTDGen vg = new VTDGen();
VTDGen vgHen = new VTDGen();
boolean headercheck = true;
if (vg.parseFile("src/main/resources/input/partitioner/" + resource.getFilename(), false)) {
VTDNav vn = vg.getNav();
AutoPilot ap = new AutoPilot(vn);
ap.selectXPath("/root/Payment");
// flb contains all the offset and length of the segments to be skipped
FastLongBuffer flb = new FastLongBuffer(4);
int i;
byte[] xml = vn.getXML().getBytes();
while ((i = ap.evalXPath()) != -1) {
flb.append(vn.getElementFragment());
}
int size = flb.size();
log.info("Payment Size {}", size);
if (size != 0) {
for (int k = 0; k < size; k++) {
String message = new String(xml, flb.lower32At(k), flb.upper32At(k), StandardCharsets.UTF_8);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
Payment payment = objectMapper
.readValue(message, Payment.class);
paymentList.add(pcPayment);
index = pcPaymentList.size() + 1;
}
}
log.info("Payment List:: {}", paymentList.size());
log.info("Index::{}", index);
return index > paymentList .size() ? null : (T) paymentList;
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
SpringBatch ConfigClass
private final Logger logger = LoggerFactory.getLogger(SpringBatchConfig.class);
#Autowired
private JobBuilderFactory jobBuilderFactory;
#Autowired
private StepBuilderFactory stepBuilderFactory;
#Autowired
ResourcePatternResolver resoursePatternResolver;
#Bean
public Job job() {
return jobBuilderFactory.get("job").start(readpayment()).build();
}
#Bean
public JobLauncher jobLauncher() throws Exception {
SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
jobLauncher.setJobRepository(jobRepository());
jobLauncher.afterPropertiesSet();
return jobLauncher;
}
#Bean
public JobRepository jobRepository() throws Exception {
MapJobRepositoryFactoryBean factory = new MapJobRepositoryFactoryBean();
factory.setTransactionManager(new ResourcelessTransactionManager());
return (JobRepository) factory.getObject();
}
#Bean
protected Step readpayment() {
return stepBuilderFactory.get("readpayment").partitioner("paymentStep", partitioner(null))
.step(paymentStep()).taskExecutor(taskExecutor()).build();
}
#Bean
protected Step paymentStep() {
return stepBuilderFactory.get("paymentStep")
.<Payment,Payment>chunk(10)
.reader(xmlFileItemReader(null))
.writer(writer()).build();
}
#Bean
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setMaxPoolSize(10);
taskExecutor.setCorePoolSize(10);
taskExecutor.setQueueCapacity(10);
taskExecutor.afterPropertiesSet();
return taskExecutor;
}
#Bean
#StepScope
ItemReader<Payment> xmlFileItemReader(#Value("#{stepExecutionContext[fileName]}") String filename) {
VtdWholeItemReader<Payment> xmlFileReader = new VtdWholeItemReader<>();
xmlFileReader.setResource(new ClassPathResource("input/partitioner/" + filename));
return xmlFileReader;
}
#Bean
#StepScope
public CustomMultiResourcePartitioner partitioner(#Value("#{jobParameters['fileName']}") String fileName) {
logger.info("fileName {}", fileName);
CustomMultiResourcePartitioner partitioner = new CustomMultiResourcePartitioner();
Resource[] resources;
try {
resources = resoursePatternResolver.getResources("file:src/main/resources/input/partitioner/*.xml");
} catch (IOException e) {
throw new RuntimeException("I/O problems when resolving the input file pattern.", e);
}
partitioner.setResources(resources);
return partitioner;
}
#Bean
public ItemWriter<Payment> writer() {
return new PaymentItemWriter();
}
PaymentItemWriter
#Override
public void write(List<? extends List<Payment>> items) throws Exception {
log.info("Items {}", items.size());
}
May be try making ItemWriter bean as step scope ["#StepScope"] in Spring batch configuration class
Make your xmlFileItemReader method return the actual type VtdWholeItemReader instead of the interface type ItemReader:
#Bean
#StepScope
VtdWholeItemReader<Payment> xmlFileItemReader(#Value("#{stepExecutionContext[fileName]}") String filename) {
VtdWholeItemReader<Payment> xmlFileReader = new VtdWholeItemReader<>();
xmlFileReader.setResource(new ClassPathResource("input/partitioner/" + filename));
return xmlFileReader;
}
This way, Spring will correctly proxy your reader as an ItemStreamReader (and not ItemReader) and honor the contract of calling open/update/close methods.

how can multiple users send message with rabbitmq using spring boot

My aim: i have multiple jobs(Processes) running parallely (seperate threads). i want to implement messaging so that each process can send message(if required) to rabbitmq Server.
now i have this
#Configuration
public class SenderConfiguration {
String content = "";
String host = "";
String port = "";
String userName = "";
String password = "";
String queueName = "";
InputStream input = null;
public SenderConfiguration() {
init();
}
private void init() {
Properties prop = new Properties();
try {
input = new FileInputStream("R.CONFIGURATION_FILE_PATH");
host = prop.getProperty("messaging.host");
port = prop.getProperty("messaging.port");
userName = prop.getProperty("messaging.userName");
password = prop.getProperty("messaging.password");
queueName = prop.getProperty("messaging.queue");
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
if (input != null) {
try {
input.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
#Bean
public RabbitTemplate rabbitTemplate() {
RabbitTemplate template = new RabbitTemplate(connectionFactory());
template.setRoutingKey(this.queueName);
return template;
}
#Bean
public ConnectionFactory connectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory(
this.host);
connectionFactory.setUsername(userName);
connectionFactory.setPassword(password);
return connectionFactory;
}
#Bean
public ScheduledProducer scheduledProducer() {
return new ScheduledProducer();
}
#Bean
public BeanPostProcessor postProcessor() {
return new ScheduledAnnotationBeanPostProcessor();
}
static class ScheduledProducer {
#Autowired
private volatile RabbitTemplate rabbitTemplate;
private final AtomicInteger counter = new AtomicInteger();
#Scheduled(fixedRate = 1000)
public void sendMessage(String message) {
rabbitTemplate.convertAndSend("Roxy " + counter.incrementAndGet());
}
}
}
and to call this from one of my operation
new AnnotationConfigApplicationContext(SenderConfiguration.class);
shall i make it abstract class and my every operation /process should extend it ? what would be best approach ?
and can i make above process any better?
Just use single class with property placeholders...
Use
#Value("${messaging.host}")
String host;
etc.
No need for a subclass for each.

How to test DAO which used unable class?

I have to write some dao tests for project where I want to:
create DDL schema from database (MySQL);
create tables in another test database in memory (H2);
insеrt some data to database;
select the just inserted item;
check some data from this item.
This is my test:
public class BridgeDBTest {
private static String JDBC_DRIVER;
private static String JDBC_URL;
private static String USER;
private static String PSWD;
private static final Logger logger = LoggerFactory.getLogger(BridgeDBTest.class);
#BeforeGroups(groups = "bridgeDB")
public void init(){
try {
JDBC_DRIVER = org.h2.Driver.class.getName();
JDBC_URL = "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1";
USER = "root";
PSWD = "";
new HibernateTestUtil().setDialect("org.hibernate.dialect.HSQLDialect")
.translateCreateDllToOutputStream(new FileOutputStream(new File("src/test/resources/createSchema.sql")));
RunScript.execute(JDBC_URL, USER, PSWD, "src/test/resources/createSchema.sql", Charset.forName("UTF8"), false);
insertDataset(readDataSet());
}
catch (Exception expt) {
expt.printStackTrace();
logger.error("!!!" + expt);
throw new RuntimeException(expt.getMessage());
}
}
#Test(groups = "bridgeDB")
public void getItem(){
BridgeDAOImpl dao = new BridgeDAOImpl();
dao.setSessionFactory(new HibernateTestUtil().getSessionFactory());
try {
Bridge bridge = dao.get(1L);
assert(bridge.getName().equals("TEST-CN-DEVBOX01"));
} catch (ServiceException e) {
e.printStackTrace();
}
}
#AfterGroups(groups = "bridgeDB")
public void dropTables(){
try {
new HibernateTestUtil().setDialect("org.hibernate.dialect.HSQLDialect")
.translateDropDllToOutputStream(new FileOutputStream(new File("src/test/resources/dropSchema.sql")));
}
catch (Exception expt) {
expt.printStackTrace();
logger.error("!!!" + expt);
throw new RuntimeException(expt.getMessage());
}
}
private IDataSet readDataSet() throws Exception{
return new FlatXmlDataSetBuilder().build(new File("src/test/resources/datasetForTest.xml"));
}
private void insertDataset(IDataSet dataSet) throws Exception{
IDatabaseTester databaseTester = new JdbcDatabaseTester(JDBC_DRIVER, JDBC_URL, USER, PSWD);
databaseTester.setSetUpOperation(DatabaseOperation.CLEAN_INSERT);
databaseTester.setDataSet(dataSet);
databaseTester.onSetup();
}
}
BridgeDAOImplused class HibernateUtilfrom src/main/..., but I need to use my class HibernateTestUtil from src/test/.... It's modified HibernateUtil fitted for my test (there I set parameters for Configuration class).
BridgeDAOImpl (See 5 line in try block):
public class BridgeDAOImpl extends GenericDAOImpl<Bridge, Long> implements BridgeDAO {
//...
public SearchResult<Bridge> list(int from, int limit, String filter, String order, Long authId) throws ServiceException {
SearchResult<Bridge> results = null;
Search search = new Search(Bridge.class);
Session session = getSessionFactory().getCurrentSession();
Transaction transaction = null;
try {
transaction = session.beginTransaction();
search.setFirstResult(from);
search.setMaxResults(limit);
HibernateUtil.buildSearch(filter, order, search, aliases);
results = searchAndCount(search);
transaction.commit();
}
catch (Exception expt) {
logger.error("!!!", expt);
if (transaction != null) {
transaction.rollback();
}
throw new ServiceException(expt.getMessage());
}
finally {
// session.close();
}
return results;
}
//...
}
How I can test my dao without modifying it?

easymock expect not working and still calling actual method

I want to test MessageProcessor1.listAllKeyword method, which in turn
calls HbaseUtil1.getAllKeyword method. Initialy, I had to deal with a problem associated with the static initializer and the constructor. The problem was to initialize a HBASE DB connection. I used powerMock to suppress static and constructor calls and it worked fine.
Even though I mocked HbaseUtil1.getAllKeyword method, actual method is being called and executes all HBase code leading to an exception, in which HBASE server is not up.
EasyMock.expect(hbaseUtil.getAllKeyword("msg", "u1")).andReturn(expectedList);
Please give me any idea on how to avoid an actual method call. I tried many ways but none of them worked.
public class MessageProcessor1
{
private static Logger logger = Logger.getLogger("MQ-Processor");
private final static String CLASS_NAME = "MessageProcessor";
private static boolean keywordsTableExists = false;
public static PropertiesLoader props;
HbaseUtil1 hbaseUtil;
/**
* For checking if table exists in HBase. If doesn't exists, will create a
* new table. This runs only once when class is loaded.
*/
static {
props = new PropertiesLoader();
String[] userTablefamilys = {
props.getProperty(Constants.COLUMN_FAMILY_NAME_COMMON_KEYWORDS),
props.getProperty(Constants.COLUMN_FAMILY_NAME_USER_KEYWORDS) };
keywordsTableExists = new HbaseUtil()
.creatTable(props.getProperty(Constants.HBASE_TABLE_NAME),
userTablefamilys);
}
/**
* This will load new configuration every time this class instantiated.
*/
{
props = new PropertiesLoader();
}
public String listAllKeyword(String userId) throws IOException {
HbaseUtil1 util = new HbaseUtil1();
Map<String, List<String>> projKeyMap = new HashMap<String, List<String>>();
//logger.info(CLASS_NAME+": inside listAllKeyword method");
//logger.debug("passed id : "+userId);
List<String> qualifiers = util.getAllKeyword("msg", userId);
List<String> keywords = null;
for (String qualifier : qualifiers) {
String[] token = qualifier.split(":");
if (projKeyMap.containsKey(token[0])) {
projKeyMap.get(token[0]).add(token[1]);
} else {
keywords = new ArrayList<String>();
keywords.add(token[1]);
projKeyMap.put(token[0], keywords);
}
}
List<Project> projects = buildProject(projKeyMap);
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation()
.create();
System.out.println("Json projects:::" + gson.toJson(projects));
//logger.debug("list all keyword based on project::::"+ gson.toJson(projects));
//return gson.toJson(projects);
return "raj";
}
private List<Project> buildProject(Map<String, List<String>> projKeyMap) {
List<Project> projects = new ArrayList<Project>();
Project proj = null;
Set<String> keySet = projKeyMap.keySet();
for (String hKey : keySet) {
proj = new Project(hKey, projKeyMap.get(hKey));
projects.add(proj);
}
return projects;
}
//#Autowired
//#Qualifier("hbaseUtil1")
public void setHbaseUtil(HbaseUtil1 hbaseUtil) {
this.hbaseUtil = hbaseUtil;
}
}
public class HbaseUtil1 {
private static Logger logger = Logger.getLogger("MQ-Processor");
private final static String CLASS_NAME = "HbaseUtil";
private static Configuration conf = null;
public HbaseUtil1() {
PropertiesLoader props = new PropertiesLoader();
conf = HBaseConfiguration.create();
conf.set(HConstants.ZOOKEEPER_QUORUM, props
.getProperty(Constants.HBASE_CONFIGURATION_ZOOKEEPER_QUORUM));
conf.set(
HConstants.ZOOKEEPER_CLIENT_PORT,
props.getProperty(Constants.HBASE_CONFIGURATION_ZOOKEEPER_CLIENT_PORT));
conf.set("hbase.zookeeper.quorum", props
.getProperty(Constants.HBASE_CONFIGURATION_ZOOKEEPER_QUORUM));
conf.set(
"hbase.zookeeper.property.clientPort",
props.getProperty(Constants.HBASE_CONFIGURATION_ZOOKEEPER_CLIENT_PORT));
}
public List<String> getAllKeyword(String tableName, String rowKey)
throws IOException {
List<String> qualifiers = new ArrayList<String>();
HTable table = new HTable(conf, tableName);
Get get = new Get(rowKey.getBytes());
Result rs = table.get(get);
for (KeyValue kv : rs.raw()) {
System.out.println("KV: " + kv + ", keyword: "
+ Bytes.toString(kv.getRow()) + ", quaifier: "
+ Bytes.toString(kv.getQualifier()) + ", family: "
+ Bytes.toString(kv.getFamily()) + ", value: "
+ Bytes.toString(kv.getValue()));
qualifiers.add(new String(kv.getQualifier()));
}
table.close();
return qualifiers;
}
/**
* Create a table
*
* #param tableName
* name of table to be created.
* #param familys
* Array of the name of column families to be created with table
* #throws IOException
*/
public boolean creatTable(String tableName, String[] familys) {
HBaseAdmin admin = null;
boolean tableCreated = false;
try {
admin = new HBaseAdmin(conf);
if (!admin.tableExists(tableName)) {
HTableDescriptor tableDesc = new HTableDescriptor(tableName);
for (int i = 0; i < familys.length; i++) {
tableDesc.addFamily(new HColumnDescriptor(familys[i]));
}
admin.createTable(tableDesc);
System.out.println("create table " + tableName + " ok.");
}
tableCreated = true;
admin.close();
} catch (MasterNotRunningException e1) {
e1.printStackTrace();
} catch (ZooKeeperConnectionException e1) {
e1.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return tableCreated;
}
}
Below is my Test class.
#RunWith(PowerMockRunner.class)
#PrepareForTest(MessageProcessor1.class)
#SuppressStaticInitializationFor("com.serendio.msg.mqProcessor.MessageProcessor1")
public class MessageProcessorTest1 {
private MessageProcessor1 messageProcessor;
private HbaseUtil1 hbaseUtil;
#Before
public void setUp() {
messageProcessor = new MessageProcessor1();
hbaseUtil = EasyMock.createMock(HbaseUtil1.class);
}
#Test
public void testListAllKeyword(){
List<String> expectedList = new ArrayList<String>();
expectedList.add("raj:abc");
suppress(constructor(HbaseUtil1.class));
//suppress(method(HbaseUtil1.class, "getAllKeyword"));
try {
EasyMock.expect(hbaseUtil.getAllKeyword("msg", "u1")).andReturn(expectedList);
EasyMock.replay();
assertEquals("raj", messageProcessor.listAllKeyword("u1"));
} catch (IOException e) {
e.printStackTrace();
}catch (Exception e) {
e.printStackTrace();
}
}
}
The HbaseUtil1 is instantiated within the listAllKeyword method
public String listAllKeyword(String userId) throws IOException {
HbaseUtil1 util = new HbaseUtil1();
...
So the mock one you create in your test isn't being used at all.
If possible, make the HbaseUtil1 object passable, or settable on the MessageProcessor1 class and then set it in the test class.
Also, and note I'm not 100% familiar with PowerMock, you could include HbaseUtil1 in the prepare for test annotation. I think that will make PowerMock instantiate mocks instead of real objects and then use the expectations you provide in you test.

Categories

Resources