I m a newbie to spring boot tests.Here is my situation:
I want to run spring boot test but exclude some components like component contains #Aspect annotation.Because in my test case, some code will be cut in by aspect component and cause NullPointException.
I tried to use #SpringBootTest parameter classes like this
#SpringBootTest(classes=TestApplication.class) ,and TestApplication.class is a springboot Main class with #ComponentScan annotation for scan components exclude #Aspect class.I think it is not a clear way to solve this problem and it does not work for me, can anybody help me?
1.Test Case: please look at comment flag 1. that is a query database operation
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class CheckCardFormatTest{
#Autowired
private XxxServiceImpl xxxService; // service layer
....
#Test
public void testMainCardFormat() {
String result=xxxService.query("someParam");// 1.
....
}
....
}
2.here is the problem please look at comment 2, getRequest() method will cause NullPointException.Because my aspect class will interrupt query-database operation
#Aspect
#Component
public class AbcAspect {
#Around(value = "execution(*com.xx.preparedStatement_execute(..))")
public Object druidIntercept(ProceedingJoinPoint pjp) throws Throwable {
....
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
HttpServletRequest request = servletRequestAttributes.getRequest();// 2.
....
}
add #TestComponent to AbcsAspect class
here is the javadoc of #TestComponent
/**
* {#link Component #Component} that can be used when a bean is intended only for tests,
* and should be excluded from Spring Boot's component scanning.
*
* Note that if you directly use {#link ComponentScan #ComponentScan} rather than relying
* on {#code #SpringBootApplication} you should ensure that a {#link TypeExcludeFilter} is
* declared as an {#link ComponentScan#excludeFilters() excludeFilter}.
*
* #author Phillip Webb
* #since 1.4.0
* #see TypeExcludeFilter
* #see TestConfiguration
*/
Related
I have got few dynamic Kafka consumers (based upon the department id, etc..) and you can find the code below.
Basically, I wanted to log the time taken for each onMessage() method call and so I have created a #LogExecutionTime method level custom annotation and added it for onMessage() method .
But my logExecutionTime() of LogExecutionTimeAspect never gets called even though my onMessage() is being invoked whenever there is a message on to the topic and everything else works fine.
Could you please help on what am I missing LogExecutionTimeAspect class so that it starts working?
LogExecutionTime:
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface LogExecutionTime {
}
LogExecutionTimeAspect class:
#Aspect
#Component
public class LogExecutionTimeAspect {
#Around("within(com.myproject..*) && #annotation(LogExecutionTime)")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
Object object = joinPoint.proceed();
long endTime = System.currentTimeMillis();
System.out.println(" Time taken by Listener ::"+(endTime-startTime)+"ms");
return object;
}
}
DepartmentsMessageConsumer class:
#Component
public class DepartmentsMessageConsumer implements MessageListener {
#Value(value = "${spring.kafka.bootstrap-servers}" )
private String bootstrapAddress;
#PostConstruct
public void init() {
Map<String, Object> consumerProperties = new HashMap<>();
consumerProperties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG,
bootstrapAddress);
consumerProperties.put(ConsumerConfig.GROUP_ID_CONFIG, "DEPT_ID_HERE");
ContainerProperties containerProperties =
new ContainerProperties("com.myproj.depts.topic");
containerProperties.setMessageListener(this);
DefaultKafkaConsumerFactory<String, Greeting> consumerFactory =
new DefaultKafkaConsumerFactory<>(consumerProperties,
new StringDeserializer(),
new JsonDeserializer<>(Department.class));
ConcurrentMessageListenerContainer container =
new ConcurrentMessageListenerContainer<>(consumerFactory,
containerProperties);
container.start();
}
#Override
#LogExecutionTime
public void onMessage(Object message) {
ConsumerRecord record = (ConsumerRecord) message;
Department department = (Department)record.value();
System.out.println(" department :: "+department);
}
}
ApplicationLauncher class:
#SpringBootApplication
#EnableKafka
#EnableAspectJAutoProxy
#ComponentScan(basePackages = { "com.myproject" })
public class ApplicationLauncher extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(ApplicationLauncher.class, args);
}
}
EDIT:
I have tried #EnableAspectJAutoProxy(exposeProxy=true), but did not work.
You should consider to turn on this option on the #EnableAspectJAutoProxy:
/**
* Indicate that the proxy should be exposed by the AOP framework as a {#code ThreadLocal}
* for retrieval via the {#link org.springframework.aop.framework.AopContext} class.
* Off by default, i.e. no guarantees that {#code AopContext} access will work.
* #since 4.3.1
*/
boolean exposeProxy() default false;
On the other hand there is something like this, which is going to be better than AOP:
/**
* A plugin interface that allows you to intercept (and possibly mutate) records received by the consumer. A primary use-case
* is for third-party components to hook into the consumer applications for custom monitoring, logging, etc.
*
* <p>
* This class will get consumer config properties via <code>configure()</code> method, including clientId assigned
* by KafkaConsumer if not specified in the consumer config. The interceptor implementation needs to be aware that it will be
* sharing consumer config namespace with other interceptors and serializers, and ensure that there are no conflicts.
* <p>
* Exceptions thrown by ConsumerInterceptor methods will be caught, logged, but not propagated further. As a result, if
* the user configures the interceptor with the wrong key and value type parameters, the consumer will not throw an exception,
* just log the errors.
* <p>
* ConsumerInterceptor callbacks are called from the same thread that invokes {#link org.apache.kafka.clients.consumer.KafkaConsumer#poll(long)}.
* <p>
* Implement {#link org.apache.kafka.common.ClusterResourceListener} to receive cluster metadata once it's available. Please see the class documentation for ClusterResourceListener for more information.
*/
public interface ConsumerInterceptor<K, V> extends Configurable {
UPDATE
#EnableAspectJAutoProxy(exposeProxy=true) did not work and I know that I could use interceptor, but I wanted to make it working with AOP.
Then I suggest you to consider to separate a DepartmentsMessageConsumer and the ConcurrentMessageListenerContainer. I mean move that ConcurrentMessageListenerContainer into the separate #Configuration class. The ApplicationLauncher is a good candidate. Make it as a #Bean and dependent on your DepartmentsMessageConsumer for injection. The point is that you need to give an AOP a chance to instrument your DepartmentsMessageConsumer, but with the #PostConstruct, that's too early to instantiate and start consumption from Kafka.
I'd like to set some default values in the session in a SpringBoot application. Ideally, I was thinking to use a class annotated with #ControllerAdvice to set the default values. This is useful, especially because the code snippet must be executed for all the pages.
Is there a way to access the HttpSession in a class annotated with #ControllerAdvice?
You can get the session from within your #ControllerAdvice, using:
Option 1:
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
HttpSession session = requeset.getSession(true);//true will create if necessary
Option 2:
#Autowired(required=true)
private HttpServletRequest request;
Option 3:
#Context
private HttpServletRequest request;
Here is an example of how I have devined a Controller aspect that intercepts all controller endpoint methods:
#Component
#Aspect
class ControllerAdvice{
#Pointcut("#annotation(org.springframework.web.bind.annotation.RequestMapping)")
void hasRequestMappingAnnotation() {}
#Pointcut("execution(* your.base.package..*Controller.*(..))")
void isMethodExecution() {}
/**
* Advice to be executed if this is a method being executed in a Controller class within our package structure
* that has the #RequestMapping annotation.
* #param joinPoint
* #throws Throwable
*/
#Before("hasRequestMappingAnnotation() && isMethodExecution()")
void beforeRequestMappedMethodExecution(JoinPoint joinPoint) {
String method = joinPoint.getSignature().toShortString();
System.out.println("Intercepted: " + method);
//Now do whatever you need to
}
}
I would recommend you to use Spring Interceptors rather then #ControllerAdvice.
Later you can easily customize behaviour with Interceptor mappings.
http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#mvc-handlermapping-interceptor
#ControllerAdvice really shines, when you want to handle some exceptions globally.
The web applications that we write require bilingual urls. We previously used "plain Spring/Spring MVC", but we've started transitioning to Spring boot.
In our older Spring 3.x/4.x applications, we solved this problem by unwinding most of the "autoconfig" in order to override the getMappingPathPatterns to include a translated version of the URL. For example:
#Override
protected Set<String> getMappingPathPatterns(RequestMappingInfo info) {
Set<String> unilingualPatterns = super.getMappingPathPatterns(info);
Set<String> bilingualPatterns = new HashSet<String>();
for (String pattern : unilingualPatterns) {
bilingualPatterns.add(pattern);
// Create the French and English URLs.
// "resolver" translates the english URL to French.
final String fraURL = resolver.convertUrl(pattern, Locale.CANADA_FRENCH);
if (!pattern.equals(fraURL)) {
bilingualPatterns.add(fraURL);
}
}
return bilingualPatterns;
}
This way, a controller that has a method like this:
#RequestMapping(value = "/home")
public String homeView() {
return "home";
}
Would automatically have a 2nd mapping of "/accueil". In essence it would be as if the method were actually annotated like this:
#RequestMapping(value = {"/home", "/accueil"})
public String homeView() {
return "home";
}
As I mentioned above, this required unwinding a lot of the "auto config", which made for a much more complicated setup.
We're starting to use Spring Boot for new projects, and one of our goals is to reduce the complexity of our configuration. I was hoping to find a cleaner way to include this functionality.
I've tried creating my own RequestMappingHandlerMapping bean in a Configuration class, but it doesn't get used. In fact, it seems Spring doesn't really want you to override this class anyhow.
After that, I tried getting a hold of the RequestMappingHandlerMapping that Spring Boot creates, iterating through the list of handlerMethods, and adding new ones for translated PatternsRequestCondition, but the map that I get back from RequestMappingHandlerMapping.getHandlerMethods() is unmodifiable, so that's a dead end as well.
I feel like I'm going down the wrong path anyhow. I've found that when using Spring, if my solution is getting complicated (like this one is), then I'm doing it wrong and I just need to find the "easy way".
If anybody has any idea of how I can manually add add new HandlerMethods (basically duplicating the existing ones, but with translated URL patterns), that would be fantastic.
Thanks in advance.
After trying a number of different solutions, the one that seemed the "most standard" was to use a single properties file for URLs. This is how I accomplished this with Spring Boot.
First, I created a new properties file called urls.properties under scr/main/resources that would contain the urls for my application, for example:
url.home=/home
url.start=/start
I added a #PropertySource annotation to my WebConfig configuration file:
/**
* Web configuration.
*
* Since 1.0.0
*/
#Configuration
#PropertySource("classpath:i18n/urls.properties")
public class WebConfig extends WebMvcConfigurerAdapter {
/**
* Locale resolver.
*
* #return <code>LocaleResolver</code>
*/
#Bean
public LocaleResolver localeResolver() {
SessionLocaleResolver localeResolver = new SessionLocaleResolver();
localeResolver.setDefaultLocale(Locale.CANADA);
return localeResolver;
}
/**
* Locale change interceptor.
*
* #return <code>LocaleChangeInterceptor</code>
*/
#Bean
public LocaleChangeInterceptor localeChangeInterceptor() {
LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
localeChangeInterceptor.setParamName("lang");
return localeChangeInterceptor;
}
/** {#inheritDoc} */
#Override
public void addInterceptors(InterceptorRegistry registry) {
super.addInterceptors(registry);
registry.addInterceptor(localeChangeInterceptor());
}
}
This gives me access to these properties in my controllers.
/**
* Demo API controller.
*
* #since 1.0.0
*/
#Controller
#RequestMapping("${url.home}")
public class DemoController {
/**
* Demo GET method binding.
*
* #return <code>String</code>
*/
#RequestMapping(method = RequestMethod.GET)
public String getHome() {
return "home";
}
}
And made sure the urls.properties properties file was listed as a property source in my application.properties file.
spring.messages.basename=messages,urls
This gives me access to these urls in my Thymeleaf templates. For example:
<a th:href="#{#{url.home}}">Home</a>
Having the #PropertySource annotation also provides easy access to the urls in test cases, for example:
/**
* DemoController test class.
*
* #since 1.0.0
*/
public class DemoControllerTest extends AbstractMockMvcTest {
#Value("${url.home}")
private String urlHome;
/**
* Test get username.
*
* #throws Exception Exception
*/
#Test
public void getUsername() throws Exception {
// Request.
resultActions = mockMvc.perform(get(urlHome));
// Verify response.
resultActions.andExpect(status().isOk());
}
}
Now, when it's time to translate the urls, just the urls.properties file needs to be sent off for translation. It's important to note that this will still just be a single file (no _en and _fr variants), so the file will end up looking like:
url.home=/home-accueil
url.start=/start-debut
I hope this helps somebody else. It was a pain to figure out the best way to make this work.
If somebody else comes up with a better way to do this (that doesn't require hacking Spring too much), please let me know!
Thanks.
In my application, I am getting hold of spring ApplicationContext inside Struts2 action, using ApplicationContextAware interface. To invoke the Service method(Spring service), I am using the below code
private ABCService abcService;
abcService=(ABCService)ApplicationContextUility.getSpringBean("ABCSeviceImpl");
/**
* #return the abcService
*/
public ABCService getAbcService() {
return abcService;
}
/**
* #param abcService the abcService to set
*/
public void setAbcService(ABCService abcService) {
this.abcService = abcService;
}
Here even if I cast it to ABCServiceImpl, instead of ABCService or I remove the getter/setter methods for abcService, code flow works. Can someone throw some light on this?
I found the answer. Both the cases the bean would get injected. But if we cast it to ABCServiceImpl instead of ABCService, proxy wont work. Incase you want to intercept this class using proxy.
I have a service class implemented in Java 6 / Spring 3 that needs an annotation to restrict access by role.
I have defined an annotation called RequiredPermission that has as its value attribute one or more values from an enum called OperationType:
public #interface RequiredPermission {
/**
* One or more {#link OperationType}s that map to the permissions required
* to execute this method.
*
* #return
*/
OperationType[] value();}
public enum OperationType {
TYPE1,
TYPE2;
}
package com.mycompany.myservice;
public interface MyService{
#RequiredPermission(OperationType.TYPE1)
void myMethod( MyParameterObject obj );
}
package com.mycompany.myserviceimpl;
public class MyServiceImpl implements MyService{
public myMethod( MyParameterObject obj ){
// do stuff here
}
}
I also have the following aspect definition:
/**
* Security advice around methods that are annotated with
* {#link RequiredPermission}.
*
* #param pjp
* #param param
* #param requiredPermission
* #return
* #throws Throwable
*/
#Around(value = "execution(public *"
+ " com.mycompany.myserviceimpl.*(..))"
+ " && args(param)" + // parameter object
" && #annotation( requiredPermission )" // permission annotation
, argNames = "param,requiredPermission")
public Object processRequest(final ProceedingJoinPoint pjp,
final MyParameterObject param,
final RequiredPermission requiredPermission) throws Throwable {
if(userService.userHasRoles(param.getUsername(),requiredPermission.values()){
return pjp.proceed();
}else{
throw new SorryButYouAreNotAllowedToDoThatException(
param.getUsername(),requiredPermission.value());
}
}
The parameter object contains a user name and I want to look up the required role for the user before allowing access to the method.
When I put the annotation on the method in MyServiceImpl, everything works just fine, the pointcut is matched and the aspect kicks in. However, I believe the annotation is part of the service contract and should be published with the interface in a separate API package. And obviously, I would not like to put the annotation on both service definition and implementation (DRY).
I know there are cases in Spring AOP where aspects are triggered by annotations one interface methods (e.g. Transactional). Is there a special syntax here or is it just plain impossible out of the box.
PS: I have not posted my spring config, as it seems to be working just fine. And no, those are neither my original class nor method names.
PPS: Actually, here is the relevant part of my spring config:
<aop:aspectj-autoproxy proxy-target-class="false" />
<bean class="com.mycompany.aspect.MyAspect">
<property name="userService" ref="userService" />
</bean>
If I understand you correct, you want a pointcut that finds all methods in classes that extends MyService and is annotated and with the preferred arguments.
I propose that you replace:
execution(public * com.mycompany.myserviceimpl.*(..))
with:
execution(public * com.mycompany.myservice.MyService+.*(..))
The plus sign is used if you want a joinpoint to match the MyService class or a class that extends it.
I hope it helps!
Espen, your code works only for one class:
execution(public * com.mycompany.myservice.MyService+.*(..))
but what if I want this behaviour for all services in *com.mycompany.services.** package?