Unable to use Aspect around over a method - java

This is in a Spring project where I have Aspect setup in a different module on which other modules are dependant on. I am using custom annotations. It works fine on some methods but not on others. Could I get advice on what the problem is?
This Aspect class is coming from a module and other modules are dependant on this.
#Aspect
#Component
public class VirtualizeJsonAspect {
#Around("#annotation(virtualizeJson)")
public Object virtualizeJsonAround(ProceedingJoinPoint pjp, VirtualizeJson virtualizeJson) throws Throwable {
MethodSignature signature = (MethodSignature) pjp.getSignature();
Method method = signature.getMethod();
Class returnType = signature.getReturnType();
// ....... This is working fine
}
Following 3 classes in same module if relevant
It works here when I try to use Aspect
Path for reference -> com/domain/abc/service/helper/DataHelper.java
#Component
#PropertySource("classpath:something.properties")
public class DataHelper {
#VirtualizeJson(serviceNm = "ABC", virtualizedDataPath = "/url/some.json")
public SomeResponse getOffers(SomeRequest someRequest){
HttpEntity httpRequestEntity = createRequestEntity(request);
ResponseEntity<SomeResponse> httpResponseEntity;
SomeResponse someResponse = null;
// ......... Aspect works here. I do not get in here. Instead I land in the Aspect class as expected
}
}
Config file
Path for reference -> com/domain/abc/service/config/AppConfig.java
#Configuration
#ComponentScan(basePackages = {{"com.domain.virtualize"})
#EnableAspectJAutoProxy
public class AppConfig extends WebMvcConfigurationSupport{
// some bean setups not rlevant to Aspect
}
It is not working here when I try to use Aspect
Path for reference -> com/domain/abc/service/rules/CheckSomething.java
#Component
public class CheckSomething extends SomeRule {
#VirtualizeJson(serviceNm = "ABC", virtualizedDataPath = "/url/some.json")
public SomeResponse checkOffers(String someNumber){
int a = 1;
return null;
// I land inside this method which is incorrect. I shouldh have landed at the Aspect class instead
}
}
Annotation
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface VirtualizeJson {
public String serviceNm();
public String virtualizedDataPath();
}

Related

How to dynamically autowire beans in Spring with annotations?

A user can write down a url, and then, depending on the pattern of the url, my interface should use the correct implementation. Therefore, want to dynamically change my Spring's bean logic execution depending on that url that my controller receives.
Here is my controller:
#PostMapping(value = "/url",
consumes = MediaType.MULTIPART_FORM_DATA_VALUE,
produces = MediaType.TEXT_HTML_VALUE)
public ResponseEntity<InputStreamResource> parseUrl(#RequestParam String url) throws IOException {
myInterface.dosomething(url);
return ResponseEntity.ok();
}
My interface :
public interface Myterface {
void myInterface(String url);
}
and my implementations:
#Service
public class myImpl1 implements myInterface {
#Override
public void doSomething(String url) {}
}
I already tried #Qualifier, but it is not dynamic. The thing is that I will have a lot of different url patterns and therefore implementations overtime, I'd like to have to add only one class per pattern and not to have to modify anything.
You can try something like this in a configuration class or you can use #Profile annotation :
#Configuration
public class MyConfig {
#Bean
public MyInterface createBean(String URL) {
MyInterface bean;
switch(URL) {
case url1:
bean = new Implementation1();
break;
case url2:
bean = new Implementation2();
break;
default: bean = new DefaultImplementation();
}
return bean;
}
}
Check this answer for more details.

Alternative to ApplicationContext.getBean() in Spring

I am using SpringBoot in my application and am currently using applicationContext.getBean(beanName,beanClass) to get my bean before performing operations on it. I saw in a couple of questions that it is discouraged to use getBean(). Since I am very new to Spring I don't know all the best practices and am conflicted. The solutions posed in the above linked question probably won't work in my use case. How should I approach this?
#RestController
#RequestMapping("/api")
public class APIHandler {
#Value("${fromConfig}")
String fromConfig;
private ApplicationContext applicationContext;
public Bot(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
#PostMapping(value = "")
public ResponseEntity post(#RequestBody HandlingClass requestBody) {
SomeInterface someInterface = applicationContext.getBean(fromConfig, SomeInterface.class);
someInterface.doSomething();
}
}
I have an interface called SomeInterface defined like:
public interface SomeInterface {
void doSomething();
}
And I have 2 classes which implements this interface called UseClass1 and UseClass2. My config file stores a string with the bean name of a class which I need to know in run-time and call the appropriate implementation of the method.
Any directions would be appreciated.
Since Spring 4.3 you can autowire all implementations into a Map consisting of pairs beanName <=> beanInstance:
public class APIHandler {
#Autowired
private Map<String, SomeInterface> impls;
public ResponseEntity post(#RequestBody HandlingClass requestBody) {
String beanName = "..."; // resolve from your requestBody
SomeInterface someInterface = impls.get(beanName);
someInterface.doSomething();
}
}
assuming you have two implementations like following
// qualifier can be omitted, then it will be "UseClass1" by default
#Service("beanName1")
public class UseClass1 implements SomeInterface { }
// qualifier can be omitted, then it will be "UseClass2" by default
#Service("beanName2")
public class UseClass2 implements SomeInterface { }
This is only code works for me to get beans dynamically from ApplicationContext
#Service
public class AuthenticationService {
#Autowired
private ApplicationContext сontext;
public boolean authenticate(...) {
boolean useDb = ...; //got from db
IAuthentication auth = context.getBean(useDb ? DbAuthentication.class : LdapAuthentication.class);
return auth.authenticate(...);
}
}
You can define your spring bean component with
#Profile("dev") , #Profile("test")
and inject as mention comment, then switch profile with
-Dspring.profiles.active=test jvm argument
The real question is not how to solve this, but why would you inject something different based on a configuration value?
If the answer is testing, then perhaps it's better to use #Profiles as #murat suggested.
Why are different implementations of an interface there on your classpath?
Can't you package your application in a way that only one is there for one use case? (see ContextConfiguration)
I think you should probably use a configuration class to produce your bean based on the fromConfig string value:
Your controller:
#RestController
#RequestMapping("/api")
public class APIHandler {
#Autowired
SomeInterface someInterface;
#PostMapping(value = "")
public ResponseEntity post(#RequestBody HandlingClass requestBody) {
someInterface.doSomething();
}
}
The bean producer:
#Configuration
public class SomeInterfaceProducer {
#Value("${fromConfig}")
String fromConfig;
#Bean
public SomeInterface produce() {
if (fromConfig.equals("aValueForUseClass1") {
return new UseClass1();
} else {
return new UseClass2();
}
//...
}
}
or if you have DI in UseClass1 and/or UseClass2:
#Configuration
public class SomeInterfaceProducer {
#Value("${fromConfig}")
String fromConfig;
#Bean
public SomeInterface produce(#Autowired YourComponent yourComponent) {
SomeInterface someInterface;
if (fromConfig.equals("aValueForUseClass1") {
someInterface = new UseClass1();
someInterface.setYourComponent(yourComponent);
// or directly with the constructor if you have one with yourComponent as parameter.
} else {
someInterface = new UseClass2();
someInterface.setYourComponent(yourComponent);
}
//...
}
}

PowerMockito.mockStatic() of a static method is not working correctly in Spring Boot Test

This is the setup of the test class:
#RunWith(PowerMockRunner.class)
#PowerMockIgnore("javax.management.*")
#PowerMockRunnerDelegate(SpringRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, classes = ServiceApplication.class)
#PrepareForTest({ MyClass.class })
public class ControllerTest {
#Autowired
public TestRestTemplate restTemplate;
public static MyClass myClass = Mockito.mock(MyClass.class);
#BeforeClass
public static void beforeClassSetup() throws Exception {
PowerMockito.mockStatic(MyClass.class);
BDDMockito.given(MyClass.getInstance(Mockito.anyString())).willReturn(myClass);
BDDMockito.given(myClass.foo()).willReturn("BAR");
// ...
}
.
.
.
// test cases
In configuration class of this project, for load some beans, I use this static call for generate the instance.
#Configuration
#ComponentScan(basePackages = { "package.from.another.project.in.production" })
public class Beans {
#Bean
public MyClass myClass() {
return MyClass.getInstance(K.FOO);
}
}
This is my controller that uses the bean, as well as the static call according to the parameters.
#RestController
public class Controller {
#Autowired
private MyClass myClass;
#GetMapping(path = "/")
public String doSomething() {
String filter = myClass.foo();
return filter;
}
#GetMapping(path = "/two")
public String doSomething2(#RequestParam Map<String, String> allParams) {
String accountId = allParams.get("account_id");
String filter = MyClass.getInstance(K.BAR + accountId).foo();
return filter;
}
}
The bean is autowired because its use is greater than instantiation by the getInstance() method. In addition, the instantiation by the getIntance() method is variable according to the parameter. Don't ask me why the MyClass class is like this, because the API was old and I'm slowly refactoring.
The issue is that the autowired bean is correctly mocked by PowerMockito.mockStatic(MyClass.class) and also by #MockBean (which I used initially), but the call MyClass.getInstance() in Controller.class does not work at all.
I think the problem should happen when Spring climbs its environment and does not load everything that has been correctly mocked by PowerMockito, just the classes of its beans. Can anyone help me solve this problem?
This is just a wild guess, have you tried using regular Mockito as opposed to BDDMockito? Just want to rule it out as a culprit.

How to test if #Valid annotation is working?

I have the following unit test:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = {EqualblogApplication.class})
#WebAppConfiguration
#TestPropertySource("classpath:application-test.properties")
public class PostServiceTest {
// ...
#Test(expected = ConstraintViolationException.class)
public void testInvalidTitle() {
postService.save(new Post()); // no title
}
}
The code for save in PostService is:
public Post save(#Valid Post post) {
return postRepository.save(post);
}
The Post class is marked with #NotNull in most fields.
The problem is: no validation exception is thrown.
However, this happens only in testing. Using the application normally runs the validation and throws the exception.
Note: I would like to do it automatically (on save) and not by manually validating and then saving (since it's more realistic).
This solution works with Spring 5. It should work with Spring 4 as well. (I've tested it on Spring 5 and SpringBoot 2.0.0).
There are three things that have to be there:
in the test class, provide a bean for method validation (PostServiceTest in your example)
Like this:
#TestConfiguration
static class TestContextConfiguration {
#Bean
public MethodValidationPostProcessor bean() {
return new MethodValidationPostProcessor();
}
}
in the class that has #Valid annotations on method, you also need to annotate it with #Validated (org.springframework.validation.annotation.Validated) on the class level!
Like this:
#Validated
class PostService {
public Post save(#Valid Post post) {
return postRepository.save(post);
}
}
You have to have a Bean Validation 1.1 provider (such as Hibernate Validator 5.x) in the classpath. The actual provider will be autodetected by Spring and automatically adapted.
More details in MethodValidationPostProcessor documentation
Hope that helps
This is how I did it by loading ValidationAutoConfiguration.class into context:
#SpringBootTest
#ContextConfiguration(classes = { MyComponent.class, ValidationAutoConfiguration.class
public class MyComponentValidationTest {
#Autowired
private MyComponent myComponent;
#Test
void myValidationTest() {
String input = ...;
// static import from org.assertj.core.api.Assertions
assertThatThrownBy(() -> myComponent.myValidatedMethod(input))
.isInstanceOf(ConstraintViolationException.class)
.hasMessageContaining("my error message");
}
}
And MyComponent class:
#Component
#Validated
public class MyComponent {
public void myValidatedMethod(#Size(min = 1, max = 30) String input) {
// method body
}
)

Spring + TestNG Autowiring failure - NOT due to "new"

Having an issue with Spring Autowiring. I have an Integration Test class declared as follows:
#ContextConfiguration(classes = TestConfig.class, loader = AnnotationConfigContextLoader.class)
public abstract class BaseIntegrationTest
extends AbstractTestNGSpringContextTests {
#Autowired
protected TestProperties properties;
//... more stuff here
}
The Context configuration looks like this:
#Configuration
#EnableAspectJAutoProxy
#ComponentScan(basePackages = {"com.ourcompany.myapp.it"},
excludeFilters = #ComponentScan.Filter(value = com.inin.wfm.it.config.ConfigPackageExcludeFilter.class, type = FilterType.CUSTOM))
public class TestConfig {
private TestProperties testProperties;
private PropertyService propertyService;
//This both creates and registers the bean with Spring
#Bean
public TestProperties getTestProperties() throws IOException {
if (testProperties == null) {
testProperties = new TestProperties(propertyService());
}
return testProperties;
}
#Bean
public PropertyService propertyService() throws IOException {
if (propertyService == null) {
AppAdminConfig config = new AppAdminConfig.Builder(PropertyService.getEnvironment(), TestConfigKey.ApplicationId)
.checkPropertyHasValue(GlobalConfigKey.KafkaBrokerList.key())
.checkPropertyHasValue(GlobalConfigKey.ZookeeperList.key())
.build();
propertyService = new PropertyService(config.getPropertiesConfig());
propertyService.initialize();
}
return propertyService;
}
}
And this is the bean I'm having trouble with:
#Configurable
public class TestProperties {
private PropertyService propertyService;
public TestProperties(PropertyService propertyService) {
this.propertyService = propertyService;
}
public String getCacheUri(){
return propertyService.getPropertyRegistry().getString(TestConfigKey.CacheUri.key(), Default.CACHE_URI);
}
}
I have multiple Test implementation classes that extend BaseIntegrationTest. All of them but one have valid references to their TestProperties field, but exactly one of the test implementation classes is getting a Null Pointer and throwing an NPE when an attempt is made to reference this.
So the question is, why is Spring #Autowire working fine for 3 different classes that extend the same base class, but wiring up null for the fourth? There is no additional configuration logic in any of the implementations.
For MCVE completeness, here's the bare bones of my impl class
public class CacheIT
extends BaseIntegrationTest {
#Test
public void testUserCache() throws InterruptedException, ExecutionException, TimeoutException {
String uri = properties.getCacheUri() //TODO - NPE here
}
}
I know there's not much to go on... I've been working with Spring for a long time and haven't seen it do this kind of thing before. According to everything I can see it should be working.

Categories