How to call repository service in main class and use main arguments? - spring

in my spring boot application I'm using main arguments to pass variables and need to call a method to save an object into database based on some code in main class.
I'm using main class like that:
#SpringBootApplication
public class MainClass {
#Autowired
private RecordingService recordingService;
public MainClass(String arg1, String arg2) {
this.setArg1(arg1);
this.setArg2(arg2);
}
public static void main(String args[]) {
String ip = null;
String op = null;
if (args != null & args.length > 0) {
for (int i = 0; i < args.length; i++) {
if (args[i].equalsIgnoreCase("-ip")) {
ip = args[i + 1].trim();
} else if (args[i].equalsIgnoreCase("-op")) {
op = args[i + 1].trim();
}
i++;
}
}
if (ip == null || op == null) {
System.out.println("Invalid input or output path.");
} else {
MainClass reader = new MainClass(ip, op);
Recording recording = new Recording();
.
.
some code...
.
.
this.recordingService.insertIntoDB(recording);
}
}
}
}
And this is service class:
#Service
public class RecordingService {
#Autowired
private RecordingRepository recordingRepository;
public RecordingService() {
}
public void insertToDatabase(Recording recording) {
try {
Recording recording = new Recording();
this.recordingRepository.save(recording);
} catch (Exception e) {
e.printStackTrace();
}
}
}
When I run this code I get NullPointerException, What is the mistake in this code?

You are starting the main method without Spring so Spring won't create RecordingService. It can be changed using CommandLineRunner:
#SpringBootApplication
public class MainClass implements CommandLineRunner {
#Autowired
private RecordingService recordingService;
public static void main(String[] args) {
SpringApplication.run(MainClass.class, args);
}
#Override
public void run(String... args) {
//your main code here
}
}

Related

How to test a try...finally method only been called once in SpringBoot?

I am following this article to implement a database read/write separation feature by calling different methods. However, I got the error:
Missing method call for verify(mock) here: verify(spyDatabaseContextHolder, times(1)).set(DatabaseEnvironment.READONLY);
when doing the testing.
My test case is trying to verify DatabaseEnvironment.READONLY has been set once when using TransactionReadonlyAspect AOP annotation:
// TransactionReadonlyAspectTest.java
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {LoadServiceImpl.class, TransactionReadonlyAspect.class})
public class TransactionReadonlyAspectTest {
#Autowired
private TransactionReadonlyAspect transactionReadonlyAspect;
#MockBean
private LoadServiceImpl loadService;
#Test
public void testReadOnlyTransaction() throws Throwable {
ProceedingJoinPoint mockProceedingJoinPoint = mock(ProceedingJoinPoint.class);
Transactional mockTransactional = mock(Transactional.class);
DatabaseContextHolder spyDatabaseContextHolder = mock(DatabaseContextHolder.class);
when(mockTransactional.readOnly()).thenReturn(true);
when(loadService.findById(16)).thenReturn(null);
when(mockProceedingJoinPoint.proceed()).thenAnswer(invocation -> loadService.findById(16));
transactionReadonlyAspect.proceed(mockProceedingJoinPoint, mockTransactional);
verify(spyDatabaseContextHolder, times(1)).set(DatabaseEnvironment.READONLY); // got the error: Missing method call for verify(mock)
verify(loadService, times(1)).findById(16);
assertEquals(DatabaseContextHolder.getEnvironment(), DatabaseEnvironment.UPDATABLE);
}
}
//TransactionReadonlyAspect.java
#Aspect
#Component
#Order(0)
#Slf4j
public class TransactionReadonlyAspect {
#Around("#annotation(transactional)")
public Object proceed(ProceedingJoinPoint proceedingJoinPoint,
org.springframework.transaction.annotation.Transactional transactional) throws Throwable {
try {
if (transactional.readOnly()) {
log.info("Inside method " + proceedingJoinPoint.getSignature());
DatabaseContextHolder.set(DatabaseEnvironment.READONLY);
}
return proceedingJoinPoint.proceed();
} finally {
DatabaseContextHolder.reset();
}
}
}
// DatabaseContextHolder.java
public class DatabaseContextHolder {
private static final ThreadLocal<DatabaseEnvironment> CONTEXT = new ThreadLocal<>();
public static void set(DatabaseEnvironment databaseEnvironment) {
CONTEXT.set(databaseEnvironment);
}
public static DatabaseEnvironment getEnvironment() {
DatabaseEnvironment context = CONTEXT.get();
System.out.println("context: " + context);
return CONTEXT.get();
}
public static void reset() {
CONTEXT.set(DatabaseEnvironment.UPDATABLE);
}
}
//DatabaseEnvironment.java
public enum DatabaseEnvironment {
UPDATABLE,READONLY
}
// LoadServiceImpl.java
#Service
public class LoadServiceImpl implements LoadService {
#Override
#Transactional(readOnly = true)
public LoadEntity findById(Integer Id) {
return this.loadDAO.findById(Id);
}
...
}
I just want to test DatabaseContextHolder.set(DatabaseEnvironment.READONLY) has been used once then in the TransactionReadonlyAspect finally block it will be reset to DatabaseEnvironment.UPDATABLE which make sense.
However, how to test DatabaseContextHolder.set(DatabaseEnvironment.READONLY) gets called once? Why does this error occur? Is there a better way to test TransactionReadonlyAspect?

Application.restart() return null

I'm trying to restart spring boot application after executing an application.
So I've added a context in the main class:
#SpringBootApplication
public class MainApplication {
private static ConfigurableApplicationContext context;
public static void main(String[] args) { context = SpringApplication.run(MainApplication.class, args); }
public static void restart() {
ApplicationArguments args = context.getBean(ApplicationArguments.class);
Thread thread = new Thread(() -> {
context.close();
context = SpringApplication.run(MainApplication.class, args.getSourceArgs());
});
thread.setDaemon(false);
thread.start();
}
}
To call the restart method, Here is the Service:
#EventListener(ApplicationReadyEvent.class)
#Transactional
public void saveMaths() {
int maths = 0;
List<User> users;
try {
users = userRepository.findByMathsWhereNull();
for (User user : users) {
maths = calculateMaths(user);
user.setMaths(maths);
userRepository.updateUser(maths, user.getId());
}
} finally {
restart();
}
}
public void restart() {
Application.restart();
}
But unfortunatelly, if the principal method works, I've a null pointer exception in the finally restart().
I've instanciated maths and users list just to be sure and once again, this method works. What did I do wrong?

My junit test is not #Autowiring. What am I missing

I am trying to unit test my Spring Boot project in Eclipse. The problem I am having is that my #Autowire s are being ignored.
#SpringBootTest
public class ValidateRepositoryTest {
private static final String CREATE_TBLVALIDATE_SQL_SCRIPT = "scripts/create/validate.sql";
private static final String DROP_TBLVALIDATE_SQL_SCRIPT = "scripts/drop/validate.sql";
private static final Logger logger = Logger.getLogger(ValidateRepositoryTest.class);
#Autowired
private JdbcTemplate jdbc;
#Before
public void before() throws SQLException {
if (jdbc == null) {
logger.fatal("jdbc == null in ValidateRepositoryTest.before()");
return;
}
ScriptUtils.executeSqlScript(jdbc.getDataSource().getConnection(), new ClassPathResource(CREATE_TBLVALIDATE_SQL_SCRIPT));
}
#After
public void after() throws SQLException {
if (jdbc == null) {
logger.fatal("jdbc == null in ValidateRepositoryTest.before()");
return;
}
ScriptUtils.executeSqlScript(jdbc.getDataSource().getConnection(), new ClassPathResource(DROP_TBLVALIDATE_SQL_SCRIPT));
}
#Autowired
ValidateRepository validateRepository;
#Test
public void testFindByKeyCode() {
if (jdbc == null) {
logger.fatal("validateRepository == null in ValidateRepositoryTest.testFindByKeyCode()");
return;
}
String documentTypeKeyCode = Validate.DOCUMENT_TYPE_CLAIMS_APPROVAL;
String sendMethodKeyCode = Validate.DOCUMENT_SEND_METHOD_EMAIL;
Validate validate = validateRepository.findByKeyCode(documentTypeKeyCode);
assertEquals("Shortage Claims Approval POD", validate.getDescription());
}
}
The output.
[INFO] Running com.kable.newsstand.batch.shortage_claim_auto_accept.entities.validate.test.ValidateRepositoryTest
09:40:51.029 [main] ERROR com.kable.newsstand.batch.shortage_claim_auto_accept.entities.validate.test.ValidateRepositoryTest - jdbc == null in ValidateRepositoryTest.before()
09:40:51.036 [main] ERROR com.kable.newsstand.batch.shortage_claim_auto_accept.entities.validate.test.ValidateRepositoryTest - validateRepository == null in ValidateRepositoryTest.testFindByKeyCode()
09:40:51.036 [main] ERROR com.kable.newsstand.batch.shortage_claim_auto_accept.entities.validate.test.ValidateRepositoryTest - jdbc == null in ValidateRepositoryTest.after()
I think it might have something to do with not having #RunWith(SpringRunner.class) at the top, but if I include this, it tries to run my main application, which throws exceptions because instance variables aren't populated from the environment:
#SpringBootApplication
public class ShortageClaimAutoAccept implements CommandLineRunner {
private static final Logger logger = Logger.getLogger(ShortageClaimAutoAccept.class);
private static String cognosUser;
private static String cognosPassword;
private static String smtpHost;
private static String ftpServer;
private static String ftpUserName;
private static String ftpPassword;
private static String ftpPath;
private static Mailer mailer;
private static FtpRelativePathUsage ftp;
#Autowired
JdbcTemplate jdbcTemplate;
#Autowired
ClaimRepository claimRepository;
#Autowired
ClaimDetailRepository claimDetailRepository;
#Autowired
DocumentControlRepository documentControlRepository;
#Autowired
DocumentReportRepository documentReportRepository;
#Autowired
ValidateRepository validateRepository;
private void startClaimAutoAcceptApp() {
if (smtpHost == null) {
throw new IllegalStateException("smtp host is null");
}
if (cognosUser == null) {
throw new IllegalStateException("cognos user is null");
}
if (cognosPassword == null) {
throw new IllegalStateException("cognos password is null");
}
if (ftpServer == null) {
throw new IllegalStateException("ftp host is null");
}
if (ftpUserName == null) {
throw new IllegalStateException("ftp user is null");
}
if (ftpPassword == null) {
throw new IllegalStateException("ftp password is null");
}
if (ftpPath == null) {
throw new IllegalStateException("ftp server base path is null");
}
acceptClaimDetailsAndCloseClaims();
emailPODClaims();
}
public static void main(String[] args) {
try {
final Properties props = ApplicationPropertiesProvider.getProperties();
smtpHost = props.getProperty("SYS_SMTP_HOST");
cognosUser = props.getProperty("REPORT_RUNNER_USER");
cognosPassword = props.getProperty("REPORT_RUNNER_PWD");
ftpServer = props.getProperty("FTP_I_CLAIMSPOD_HOST");
ftpUserName = props.getProperty("FTP_I_CLAIMSPOD_USRID");
ftpPassword = props.getProperty("FTP_I_CLAIMSPOD_PWD");
ftpPath = props.getProperty("FTP_I_CLAIMS_BASE_PATH");
mailer = new Mailer(smtpHost, "");
FtpSite ftpSite = new FtpSite(ftpServer, ftpUserName, ftpPassword, ftpPath);
ftp = new FtpRelativePathUsage(ftpSite);
SpringApplication.run(ShortageClaimAutoAccept.class, args);
} catch (IllegalStateException ex) {
logger.fatal(ex);
} catch (Exception ex) {
logger.fatal("Uncaught exception in the process: \n", ex);
}
}
#Override
public void run(String... arg0) throws Exception {
startClaimAutoAcceptApp();
}
}
I'm not sure why it tries to run this, when all I am doing is testing.
You are missing two things here:
A runner
#RunWith(SpringRunner.class)
The component classes to use for loading an ApplicationContext. Can also
be specified using #ContextConfiguration(classes=...). If no
explicit classes are defined the test will look for nested Configuration
classes, before falling back to a SpringBootConfiguration search.
#SpringBootTest(classes = ShortageClaimAutoAccept.class)

Why does CachePut not work in this example?

I am playing around with the Spring framework and I would like to get my name returned from the cache. After 5 seconds I will update the cache and I hope to receive a new name.... unfortunately this is not working.... why?
#Component
public class Test {
public String name = "peter";
#Cacheable(value = "numCache")
public String getName() {
return name;
}
#Scheduled(fixedRate = 5000)
#CachePut(value = "numCache")
public String setName() {
this.name = "piet";
return name;
}
}
#Component
public class AppRunner implements CommandLineRunner {
public void run(String... args) throws Exception {
Test test = new Test();
while(true) {
Thread.sleep(1000);
System.out.println(test.getName());
}
}
}
#SpringBootApplication
#EnableCaching
#EnableScheduling
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
You are creating an instance of Test yourself with new, you are not autowiring it. I would try like this:
#Component
public class Test {
public String name = "peter";
#Cacheable(value = "numCache")
public String getName() {
return name;
}
#Scheduled(fixedRate = 5000)
#CachePut(value = "numCache")
public String setName() {
this.name = "piet";
return name;
}
}
#Component
public class AppRunner implements CommandLineRunner {
#Autowired private Test test;
public void run(String... args) throws Exception {
while(true) {
Thread.sleep(1000);
System.out.println(test.getName());
}
}
}

Spring 4 - Spring retry 1.2.1.RELEASE #Recover not working

I have created an adapterImpl class that will retry a method with an objA but if it throws an exception(hardcoded to throw) it will call recover method - which will again call the method with objB.
My problem is - The #Recover method is not called. I am not sure what I am doing wrong here.
Spring version - 4.3.5.RELEASE
Spring retry - 1.2.1.RELEASE
My Config class -
#Configuration
#EnableRetry
public class ConfigClass {
#Bean
public ClassTest beanA(){
ClassTest obj = new ClassTest();
obj.setProp(5);
return obj;
}
#Bean
public ClassTest beanB(){
ClassTest obj = new ClassTest();
obj.setProp(10);
return obj;
}
#Bean("adapterImpl")
public AdapterInterfaceImpl adapter(){
AdapterInterfaceImpl obj = new AdapterInterfaceImpl();
return obj;
}
}
My AdapterInterfaceImpl class -
public class AdapterInterfaceImpl implements AdapterInterface{
#Autowired
#Qualifier("beanA")
private ClassTest objA;
#Autowired
#Qualifier("beanB")
private ClassTest objB;
public ClassTest getObjA() {
return objA;
}
public void setObjA(ClassTest objA) {
this.objA = objA;
}
public ClassTest getObjB() {
return objB;
}
public void setObjB(ClassTest objB) {
this.objB = objB;
}
#Retryable(maxAttempts = 3, include = Exception.class, backoff = #Backoff(delay = 2000))
public int getValue(int val) throws Exception{
System.out.println("obj A get Value");
return getValue(objA,val);
}
public int getValue(ClassTest obj, int val) throws Exception{
System.out.println("get Value");
if(obj==objA){
throw new Exception("This is msg");
}
return obj.methodA(val);
}
#Recover
public int getValue(Exception e, int val){
System.out.println("Recover get Value");
try{
return getValue(objB,val);
}catch(Exception e1){
return 0;
}
}
My ClassTest class -
public class ClassTest {
private int prop;
public int getProp() {
return prop;
}
public void setProp(int prop) {
this.prop = prop;
}
public int methodA(int x){
return x+prop;
}
}
My class with main method -
public class App
{
public static void main( String[] args )
{
AbstractApplicationContext context = new
AnnotationConfigApplicationContext(ConfigClass.class);
AdapterInterface adapter = (AdapterInterface)
context.getBean("adapterImpl");
try {
System.out.println(adapter.getValue(3));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
My output is not showing any retry nor recovery -
A get Value
get Value
obj A get Value
get Value
obj A get Value
get Value
org.springframework.retry.ExhaustedRetryException: Cannot locate recovery method; nested exception is java.lang.Exception: This is msg
Spring Retry uses AOP, internal calls (from getValue(int) to getValue(ClassTest, int)) won't go through the proxy.
You have to put the #Retryable on the method that is called externally so that the proxy can intercept the call and apply the retry logic.
There is a similar issue reported in https://github.com/spring-projects/spring-retry/issues/75
So #EnableRetry(proxyTargetClass=true) works as it is now able to find the recovery method in the implementation class.

Resources