I am trying to use the value of a bean at the main class but I am getting an error because it can't be used in a static context.
#SpringBootApplication
public class MigrationsApplication implements CommandLineRunner {
private static final Logger logger = LoggerFactory.getLogger(MigrationsApplication.class);
#Autowired
private UrlInterface url;
#Autowired
private UsernameInterface username;
#Autowired
private PasswordInterface password;
#Autowired
private ChangelogInterface changelog;
#Bean
public UrlInterface getUrl(#Value("${spring.datasource.url}") String dbUrl) {
return () -> dbUrl;
}
#Bean
public UsernameInterface getUsername(#Value("${spring.datasource.username}") String dbUsername) {
return () -> dbUsername;
}
#Bean
public PasswordInterface getPassword(#Value("${spring.datasource.password}") String dbPassword) {
return () -> dbPassword;
}
#Bean
public ChangelogInterface getChangelog(#Value("${spring.liquibase.change-log}") String changelogPath) {
return () -> changelogPath;
}
public static void main(String[] args) throws DatabaseException {
if (args.length < 2) {
logger.error("Please provide the desired command and the properties file");
System.exit(-1);
}
switch (args[0]) {
case "run":
ConfigurableApplicationContext ctx = SpringApplication.run(MigrationsApplication.class, args);
int exitCode = SpringApplication.exit(ctx, () -> 0);
logger.info("All migrations were finished with success");
System.exit(exitCode);
break;
case "rollback":
/*Connection connection = null;
try {
connection = openConnection(url.getUrl(),"","");
} catch (SQLException throwables) {
throwables.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
case "release":
logger.warn("RELEASE");
break;
default:
logger.error("Invalid command");
break;
}
}
#Override
public void run(String... args) throws Exception {
}
}
How can I turn around this? I can't move the code to the run method because even though it's empty it stills updates my database.
Why does the method run updates my database even if it is empty?
Is it even possible to use properties values in the Main class?
Only after SpringApplication.run is executed, the ApplicationContext instance is created and then the Beans would be loaded and are available.
To access a Bean in static main method, you could fetch it directly from the ApplicationContext. Maybe something like:
ApplicationContext ctx = SpringApplication.run(MigrationsApplication.class, args);
UrlInterface url = (UrlInterface) ctx.getBean("getUrl");
For using #Autowired with a static method. You need to use static variable and inject it with #Autowired on the constructor. See this answer for example
Related
I'm trying to make artificial CONSTRAINT violation by Spring instead of throwing exception from DB (an expert sad DB-produced errors have high performance cost):
import javax.validation.ConstraintViolation;
import javax.validation.Validator;
#Component
public class AccountValidator implements org.springframework.validation.Validator {
#Autowired
private Validator validator;
private final AccountService accountService;
public AccountValidator(#Qualifier("accountServiceAlias")AccountService accountService) {
this.accountService = accountService;
}
#Override
public boolean supports(Class<?> clazz) {
return AccountRequestDTO.class.equals(clazz);
}
#Override
public void validate(Object target, Errors errors) {
Set<ConstraintViolation<Object>> validates = validator.validate(target);
for (ConstraintViolation<Object> constraintViolation : validates) {
String propertyPath = constraintViolation.getPropertyPath().toString();
String message = constraintViolation.getMessage();
errors.rejectValue(propertyPath, "", message);
}
AccountRequestDTO account = (AccountRequestDTO) target;
if(accountService.getPhone(account.getPhone()) != null){
errors.rejectValue("phone", "", "Validator in action! This number is already in use.");
}
}
}
However, second part of validate() method never works for reasons I cant understand and always pass a call from controller to be handled in try-catch block throwing exception from DB:
public void saveAccount(AccountRequestDTO accountRequestDTO) throws Exception {
LocalDate birthday = LocalDate.parse(accountRequestDTO.getBirthday());
if (LocalDate.from(birthday).until(LocalDate.now(), ChronoUnit.YEARS) < 18) {
throw new RegistrationException("You must be 18+ to register");
}
Account account = new Account(accountRequestDTO.getName(), accountRequestDTO.getSurname(),
accountRequestDTO.getPhone(), birthday, BCrypt.hashpw
(accountRequestDTO.getPassword(), BCrypt.gensalt(4)));
account.addRole(Role.CLIENT);
try {
accountRepository.save(account);
}
catch (RuntimeException exc) {
throw new PersistenceException("Database exception: this number is already in use.");
}
}
Here's a controller method:
#PostMapping("/confirm")
public String signIn(#ModelAttribute("account") #Valid AccountRequestDTO accountRequestDTO,
BindingResult result, Model model) {
accountValidator.validate(accountRequestDTO, result);
if(result.hasErrors()) {
return "/auth/register";
}
try {
accountService.saveAccount(accountRequestDTO);
}
catch (Exception exc) {
model.addAttribute("message", exc.getMessage());
return "/auth/register";
}
return "/auth/login";
}
At service:
#Transactional(readOnly = true)
public String getPhone(String phone){
return accountRepository.getPhone(phone);
}
JpaRepository query:
#Query("SELECT phone FROM Account accounts WHERE phone=:check")
String getPhone(String check);
Tests are green:
#BeforeAll
static void prepare() {
search = new String("0000000000");
}
#BeforeEach
void set_up() {
account = new Account
("Admin", "Adminov", "0000000000", LocalDate.of(2001, 01, 01), "superadmin");
accountRepository.save(account);
}
#Test
void check_if_phone_presents() {
assertThat(accountRepository.getPhone(search).equals(account.getPhone())).isTrue();
}
#Test
void check_if_phone_not_presents() {
String newPhone = "9999999999";
assertThat(accountRepository.getPhone(newPhone)).isNull();
}
#AfterEach
void tear_down() {
accountRepository.deleteAll();
account = null;
}
#AfterAll
static void clear() {
search = null;
}
You need to register your validator.
After we've defined the validator, we need to map it to a specific
event which is generated after the request is accepted.
This can be done in three ways:
Add Component annotation with name “beforeCreateAccountValidator“.
Spring Boot will recognize prefix beforeCreate which determines the
event we want to catch, and it will also recognize WebsiteUser class
from Component name.
#Component("beforeCreateAccountValidator")
public class AccountValidator implements Validator {
...
}
Create Bean in Application Context with #Bean annotation:
#Bean
public AccountValidator beforeCreateAccountValidator () {
return new AccountValidator ();
}
Manual registration:
#SpringBootApplication
public class SpringDataRestApplication implements RepositoryRestConfigurer {
public static void main(String[] args) {
SpringApplication.run(SpringDataRestApplication.class, args);
}
#Override
public void configureValidatingRepositoryEventListener(
ValidatingRepositoryEventListener v) {
v.addValidator("beforeCreate", new AccountValidator ());
}
}
I'm New in Mocking.
I've a service I'm trying to call is let say name A, I need to test someMethod.
#Service
public class A {
private Logger logger = LoggerFactory.getLogger(getClass());
private final CoreXReader coreXReader;
#Autowired
B b;
#Autowired
C c;
#Async
public void someMethod(Config config) throws Exception {
pushConfig(config);
}
private void pushConfig(Config config) throws Exception {
String url = config.getBaseurl() + config.getId();
ABCRestClient restClient = new ABCRestClient(url);
String jobJson = restClient.callRestMethod(HttpMethod.GET, "");
}
}
sample of ABCRestClient
public class ABCRestClient {
private Logger logger = LoggerFactory.getLogger(getClass());
private String url;
public ABCRestClient(String url) {
this.url = url;
}
public String callRestMethod(HttpMethod method, String payload) throws Exception {
someresponse="example response";
return someresponse;
}
}
I'm trying to test by creating mockSpy but it still Calling its 'callRestMethod'
#RunWith(SpringRunner.class)
#SpringBootTest // (webEnvironment= SpringBootTest.WebEnvironment.RANDOM_PORT)
public class Test {
#Autowired
private A a;
private Logger logger = LoggerFactory.getLogger(getClass());
#Before
public void prepareMockDataForService() throws Exception {
ABCRestClient apiClient = new ABCRestClient(config.getBaseurl() + config.getId() );
ABCRestClient apiClientSpy=Mockito.spy(apiClient);
doReturn(getCallResponse()).when(apiClientSpy).callRestMethod(HttpMethod.GET, "");
}
#Test
public void TestPushConfig() throws Exception {
a.someMethod(StubDataGenerator.getConfig());
}
private String getCallResponse() {
return "{"msg":"sample response"}";
}
}
i'm not sure what I'm doing wrong here why its calling the actual callRestMethod as i already create a spy .
I tried using this too Mockito.doReturn(getCallResponse()).when(apiClientSpy.callRestMethod(HttpMethod.GET, ""))
Also, is there any difference in these two statement if I use it Mockito.doReturn() or directly doReturn()? In my case both seems behaving same.
Before I tried with this as well when().thenReturn(); but I read somewhere that use when().thenReturn() when you actually want to make call. Please correct if my understanding is wrong.
You can try mock instead of spy:
#RunWith(SpringRunner.class)
#SpringBootTest // (webEnvironment=
SpringBootTest.WebEnvironment.RANDOM_PORT)
public class Test {
#Autowired
private A a;
private Logger logger = LoggerFactory.getLogger(getClass());
#Before
public void prepareMockDataForService() throws Exception {
ABCRestClient apiClientSpy=Mockito.mock(ABCRestClient.class);
doReturn(getCallResponse()).when(apiClientSpy).callRestMethod(HttpMethod.GET, "");
}
#Test
public void TestPushConfig() throws Exception {
a.someMethod(StubDataGenerator.getConfig());
}
private String getCallResponse() {
return "{"msg":"sample response"}";
}
}
Here is what I'm trying to do:
#SpringBootApplication public class App {
public static final String NAME;
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
#Autowired public App(ApplicationArguments arguments) {
NAME = arguments.getSourceArgs()[0]; // ERROR (1)
}
#GetMapping("/" + NAME) public void test() { // ERROR (2)
return NAME;
}
}
The code doesn't work as written because (1) NAME cannot be assigned, and (2) annotation value for #GetMapping must be a constant expression.
I just want #GetMapping to use a value based on a command line argument. How can this be done?
in the first request. spring mvc will init the resource. so your put your dynamic url in haddlermapping.
we need three classes. MyController MyDispatcherServlet DispatcherServletCustomConfiguration
MyController.java
#Component(value="MyController")
public class MyController {
#Autowired
ClaimService claimService;
public ResponseEntity<HttpStatus> insertClaim() {
return new ResponseEntity<>(HttpStatus.OK);
}
}
MyDispatcherServlet.java. after initStrategies.put, your dynamic url into handdlerMapping.
public class MyDispatcherServlet extends DispatcherServlet {
private String url;
public MyDispatcherServlet(String url) {
super();
this.url = url;
}
#Override
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
List<HandlerMapping> handlerMappings = getHandlerMappings();
for (HandlerMapping handlerMapping : handlerMappings) {
if (handlerMapping instanceof RequestMappingHandlerMapping) {
RequestMappingHandlerMapping requestMappingHandlerMapping = ((RequestMappingHandlerMapping) handlerMapping);
RequestMappingInfo.Builder n = RequestMappingInfo
.paths(url)
.methods(RequestMethod.GET);
try {
Method method = MyController.class.getDeclaredMethod("insertClaim");
requestMappingHandlerMapping.registerMapping(n.build(), "MyController", method);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
}
}
DispatcherServletCustomConfiguration.java
#Configuration
public class DispatcherServletCustomConfiguration {
#Value("${myUrl}")
private String url;
#Bean
public DispatcherServlet dispatcherServlet() {
return new MyDispatcherServlet(url);
}
}
run with the command java -jar stackoverflow-1.0-SNAPSHOT.jar --myUrl=abcd
I want to be able to set the #JMSlistener destination from an application.properties
my code looks like this
#Service
public class ListenerService {
private Logger log = Logger.getLogger(ListenerService.class);
#Autowired
QueueProperties queueProperties;
public ListenerService(QueueProperties queueProperties) {
this.queueProperties = queueProperties;
}
#JmsListener(destination = queueProperties.getQueueName() )
public void listenQueue(String requestJSON) throws JMSException {
log.info("Received " + requestJSON);
}
}
but when building I get
Error:(25, 60) java: element value must be a constant expression
You can't reference a field within the current bean, but you can reference another bean in the application context using a SpEL expression...
#SpringBootApplication
public class So49368515Application {
public static void main(String[] args) {
SpringApplication.run(So49368515Application.class, args);
}
#Bean
public ApplicationRunner runner(JmsTemplate template, Foo foo) {
return args -> template.convertAndSend(foo.getDestination(), "test");
}
#JmsListener(destination = "#{#foo.destination}")
public void listen(Message in) {
System.out.println(in);
}
#Bean
public Foo foo() {
return new Foo();
}
public class Foo {
public String getDestination() {
return "foo";
}
}
}
You can also use property placeholders ${...}.
Using property placeholder is much easier.
#JmsListener(destination = "${mq.queue}")
public void onMessage(Message data) {
}
I want to setup an object pool in my spring application with annotation only.
I started out with this example taken from Spring docs: http://docs.spring.io/spring/docs/2.5.x/reference/aop-api.html#aop-ts-pool.
Here is how I translate the XML configuration:
#Configuration
#EnableAutoConfiguration
#ComponentScan
public class SpringObjectPoolTest {
public static void main(String[] args) throws Exception {
context = new SpringApplicationBuilder(SpringObjectPoolTest.class) //
.addCommandLineProperties(false) //
.web(false) //
.headless(false) //
.registerShutdownHook(true) //
.application() //
.run();
context.getBean(SpringObjectPoolTest.class).go();
}
#Resource(name = "pfb")
private FactoryBean<MyTask> pool;
#Resource(name="pool")
private TargetSource targetSource;
private static ConfigurableApplicationContext context;
#Bean(name = "task")
#Scope("prototype")
public MyTask createNewTask() {
return new MyTask();
}
#Bean(name = "pool")
public CommonsPoolTargetSource setupObjectPool() {
CommonsPoolTargetSource pc = new CommonsPoolTargetSource();
pc.setMaxSize(25);
pc.setTargetBeanName("task");
return pc;
}
#Bean(name = "pfb")
public ProxyFactoryBean createProxyFactoryBean() {
ProxyFactoryBean pfb = new ProxyFactoryBean();
pfb.setTargetSource(targetSource);
return pfb;
}
private void go() {
try {
pool.getObject().speak();
} catch (Exception e) {
e.printStackTrace();
}
}
}
However I get this exception:
org.springframework.beans.factory.BeanCurrentlyInCreationException:
Error creating bean with name 'pfb':
org.springframework.beans.factory.FactoryBeanNotInitializedException:
Cannot determine target class for proxy
You are a bit over engineering this. Spring already knows how to inject a proxied MyTask, there is no need to have a FactoryBean<MyTask> or to call getObject() on the pool. In "pooledTask" below Spring knows that by injecting a ProxyFactoryBean ("pfb") it will actually inject the instance that factory bean creates, not the factory bean itself. Here's how I'd do it:
#Configuration
#EnableAutoConfiguration
#ComponentScan
public class SpringObjectPoolTest {
public static void main(String[] args) throws Exception {
context = new SpringApplicationBuilder(SpringObjectPoolTest.class) //
.addCommandLineProperties(false) //
.web(false) //
.headless(false) //
.registerShutdownHook(true) //
.application() //
.run();
context.getBean(SpringObjectPoolTest.class).go();
}
private static ConfigurableApplicationContext context;
#Resource(name = "pfb")
private MyTask pooledTask;
#Resource(name="pool")
private CommonsPoolTargetSource targetSource;
#Bean(name = "task")
#Scope("prototype")
public MyTask createNewTask() {
return new MyTask();
}
#Bean(name = "pool")
public CommonsPoolTargetSource setupObjectPool() {
CommonsPoolTargetSource pc = new CommonsPoolTargetSource();
pc.setMaxSize(25);
pc.setTargetBeanName("task");
return pc;
}
#Bean(name = "pfb")
public ProxyFactoryBean createProxyFactoryBean() {
ProxyFactoryBean pfb = new ProxyFactoryBean();
pfb.setTargetSource(setupObjectPool());
return pfb;
}
private void go() {
try {
pooledTask.speak();
// getting another object from pool
MyTask someOtherTask = (MyTask) targetSource.getTarget();
// returning the object to the pool
targetSource.releaseTarget(someOtherTask);
} catch (Exception e) {
e.printStackTrace();
}
}
}