I have logic to intercept the RestTemplate and I am adding/registering that RestTemplate in a configuration file (SecurityConfiguration.java)
but I want to add that interceptor from another configuration file by getting RestTemplate object which is already registered:
public class TranslogRestTemplateCondition implements Condition {
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
boolean isRestTemplate = false;
try {
if (context.getBeanFactory() != null) {
isRestTemplate = (context.getBeanFactory().getBean(RestTemplate.class) != null);
}
} catch (BeansException e) {
return false;
}
return isRestTemplate;
}
}
Configuration class:
#Configuration
public class RestTemplateConfig {
private final MyInterceptor myInterceptor;
#Value("${com.pqr.you.rest.enabled:true}")
private boolean transEnabled;
#Autowired
public RestTemplateConfig(MyInterceptor myInterceptor) {
this.myInterceptor = myInterceptor;
}
#Autowired
private ApplicationContext appContext;
// The logic added below is not working for me
#Bean
#Conditional(TranslogRestTemplateCondition.class)
public RestTemplate addInterceptor(ApplicationContext appContext) {
RestTemplate restTemplate = appContext.getBean(RestTemplate.class);
if (transEnabled) {
List<ClientHttpRequestInterceptor> interceptors = restTemplate.getInterceptors();
interceptors.add(myInterceptor);
restTemplate.setInterceptors(interceptors);
}
return restTemplate;
}
}
Actual logic for RestTemplate, which is going to return with required interceptors, and some other values (at the time of returning this restTemplate, my interceptor also need to be add here, without over riding existing values)
OR
by taking the below restTempalte object and add MyInterceptor to restTemplate.
#Configuration
public class SecurityConfiguration {
#Bean
public AbcInterceptor abcRequestInterceptor(XyzService xyzService) {
return new AbcInterceptor("abc-app", null, xyzService);
}
// I dont want to create bean here
/*#Bean
public MyInterceptor myInterceptor() {
return new MyInterceptor();
}*/
#Bean
public RestTemplate restTemplate(AbcRequestInterceptor abcRequestInterceptor) {
RestTemplate restTemplate = new RestTemplate();
List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();
interceptors.add(abcRequestInterceptor);
//interceptors.add(myInterceptor); // I dont want to add this interceptor here
restTemplate.setInterceptors(interceptors);
return restTemplate;
}
}
I think what you need in this case is a BeanPostProcessor not a Condition class. It will allow you to modify the bean if it exists. you can create one like below instead of the RestTemplateConfiguration class and the Condition:
#Configuration
public class RestTemplatePostProcessor implements BeanPostProcessor {
#Value("${com.pqr.you.rest.enabled:true}")
private boolean transEnabled;
#Bean
MyInterceptor myInterceptor() {
return new MyInterceptor();
}
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof RestTemplate) { // or you can check by beanName if you like
final RestTemplate restTemplate = (RestTemplate) bean;
if (transEnabled) {
List<ClientHttpRequestInterceptor> interceptors = restTemplate.getInterceptors();
interceptors.add(myInterceptor());
restTemplate.setInterceptors(interceptors);
}
return restTemplate;
}
return bean;
}
}
Note that in your code you are trying to create another RestTemplate instance (named "addInterceptor"), which seems to be undesired.
Related
My Spring Boot application implements the TenantStore example for storing data in ThreadLocalTargetSource detailed in this link
#Bean(destroyMethod = "destroy")
public ThreadLocalTargetSource threadLocalTenantStore() {
ThreadLocalTargetSource result = new ThreadLocalTargetSource();
result.setTargetBeanName("tenantStore");
return result;
}
The working example allows for the TenantStore object to be set and injected by the Spring Framework. My version of the TenantFilter class described in that article sets the properties of the TenantStore object whenever a Servlet request is made
#Autowired
private TenantStore tenantStore;
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
try {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null) {
String token = (String) request.getAttribute(ACCESS_TOKEN_VALUE);
if (token != null) {
OAuth2AccessToken oAuth2AccessToken = tokenStore.readAccessToken(token);
if (oAuth2AccessToken.getAdditionalInformation() != null) {
String tenantName = (String) oAuth2AccessToken.getAdditionalInformation().get("tenant");
storeTenantInThread(tenantName);
}
}
}
chain.doFilter(request, response);
} catch (ResourceNotFoundException e) {
log.error(e.getMessage());
} finally {
clearTenant();
}
}
private void storeTenantInThread(String tenantName) {
tenantStore.setName(tenantName);
}
private void clearTenant() {
tenantStore.clear();
}
I then have a number of services where TenantStore is autowired and in each of these services the TenantStore contains the information that was populated in the doFilter() method. Except for one class. For some reason the properties of the TenantStore in this class are still null. The name of the class affected is MyCacheService and the architecture is as follows:
#RestController
#RequestMapping("/here")
public class MyController {
#Autowired
private MyService myService
#GetMapping
public ResponseEntity myGetMethod(#RequestParam("text") String text) {
myService.myMethod(text);
return new ResponseEntity(Http.OK);
}
}
#Service
public class MyService {
#Autowired
private TenantStore tenantStore;
#Autowired
private MyOtherService myOtherService;
public void myMethod(String text) {
System.out.println(tenantStore.getName()); //works - prints name
myOtherService.myOtherMethod(text);
}
}
#Service
public class MyOtherService {
#Autowired
private TenantStore tenantStore;
#Autowired
private Map<String, MyComponent> myComponents;
public void myOtherMethod(String text) {
System.out.println(tenantStore.getName()); //works - prints name
MyComponent useThisComponent = myComponents.get("componentName");
useThisComponent.myComponentMethod(text);
}
}
#Component("componentName")
public class MyComponent {
#Autowired
private TenantStore tenantStore;
#Autowired
private MyCacheService myCacheService;
public void myComponentMethod(String text) {
System.out.println(tenantStore.getName()); //works - prints name
entityAliasCacheService.myCacheMethod(String text);
}
}
#Service
public class MyCacheService {
#Autowired
private TenantStore tenantStore;
public void myCacheMethod(String text) {
System.out.println(tenantStore.getName()); //DOES NOT WORK - tenantStore object is not null but the name property is
}
}
From what I can guess, for some reason the TenantStore in MyCacheService is being populated in a different thread, though I've no idea why.
I noticed similar behaviour. I fixed the issue by adding a bean dependancy
#Service
#DependsOn("proxiedThreadLocalTargetSource") // asks Spring to first load proxy bean
public class MyCacheService {
where proxiedThreadLocalTargetSource bean is defined like in the OP's example -
#Primary
#Bean(name = "proxiedThreadLocalTargetSource")
public ProxyFactoryBean proxiedThreadLocalTargetSource(ThreadLocalTargetSource threadLocalTargetSource) {
ProxyFactoryBean result = new ProxyFactoryBean();
result.setTargetSource(threadLocalTargetSource);
return result;
}
So, by adding the dependancy, Spring knows that it should load MyCacheService bean after the proxiedThreadLocalTargetSource. Without this dependancy, I noticed that TenantStore got injected instead of the proxy bean.
Getting instance of TenantStore from org.springframework.context.ApplicationContext
First implement ApplicationContextAware like as below
#Component
public class ApplicationContextUtil implements ApplicationContextAware {
private static ApplicationContext context;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
public static ApplicationContext context() {
return context;
}
}
And your MyCacheService Will be like this:
public class MyCacheService {
public void myCacheMethod(String text) {
TenantStore tenantStore = ApplicationContextUtil.context().getBean(TenantStore.class);
System.out.println(tenantStore.getName());
}
}
I'm facing a situation where the Beans I've created are not accessible which makes me wonder where and when they are!?
I've made two Beans, one scoped singleton and other scoped request. I've made sure they are correctly implemented by autowiring them in a RestController class. And they are populated, no doubt there.
Now I have written an authorization checker class extending PreInvocationAuthorizationAdvice. Being an authorization class, I need to have access to current user's information. So I autowired current user's Bean to this class, this is the request scoped Bean. Also I need a customized ACL engine, which is autowired in a singleton manner. But when I reach the point when I need to use these two properties, they are both null!
So what are limitations on where and when I can expect a Bean to be accessible?
BTW, my #Configuration class is also annotated by #ComponentScan({"my.base.package"}) which is a parent package of my designated class including the #Autowired property.
[UPDATE]
I think I found what the problem is, but yet I'm struggling with the solution.
The class with #Autowired properties, is being instantiated as Bean itself. I think this late Bean is getting instantiated before the other Beans which it is depending on and as the result they are not yet available. Is there anyway I can specify the ordering of the Beans being instantiated?
[P.S.]
Anyone who flagged this question as "off-topic because: This question does not appear to be about programming" is so funny :)
[UPDATE]
Just an example when #Autowired property is null.
These are my configuration classes:
#Configuration
#PropertySource("/config.properties")
#ComponentScan({"my.package"})
public class AppConfig implements ApplicationContextAware
{
private ApplicationContext appContext;
#Autowired
private Environment env;
#Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException
{
this.appContext = applicationContext;
}
#Bean
public RedissonClient getRedisson()
{
//Code ommited: returning a redisson connection.
}
}
#Configuration
#ComponentScan({"my.pacakge"})
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends GlobalMethodSecurityConfiguration
{
#Bean
public AclEngine getAclEngine()
{
return new AclEngine();
}
#Autowired
private RedissonClient redisson;
#Bean
#Scope(value = "request")
public User getCurrentUser()
{
//Code ommited: retrieving the user from Redisson and returning it.
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth)
throws Exception
{
auth.authenticationProvider(authenticator());
}
#Bean
public AuthenticationProvider authenticator()
{
return new AclAuthenticationProvider();
}
#Bean
HttpSessionSecurityContextRepository getHttpSessionSecurityContextRepository()
{
HttpSessionSecurityContextRepository x = new HttpSessionSecurityContextRepository();
x.setAllowSessionCreation(false);
return x;
}
#Bean
SecurityContextPersistenceFilter getSecurityContextPersistenceFilter()
{
return new SecurityContextPersistenceFilter(getHttpSessionSecurityContextRepository());
}
#Override
protected AccessDecisionManager accessDecisionManager()
{
try {
AffirmativeBased ab = (AffirmativeBased) super.accessDecisionManager();
List<AccessDecisionVoter<? extends Object>> advs = ab.getDecisionVoters();
ResourceBasedPreInvocationAdvice expressionAdvice = new ResourceBasedPreInvocationAdvice();
List<AccessDecisionVoter<? extends Object>> toBeRemoved = new ArrayList<>();
for (AccessDecisionVoter<? extends Object> adv : advs) {
if (adv instanceof PreInvocationAuthorizationAdviceVoter) {
toBeRemoved.add(adv);
}
}
for (AccessDecisionVoter<? extends Object> adv : toBeRemoved) {
advs.remove(adv);
}
advs.add(new PreInvocationAuthorizationAdviceVoter(expressionAdvice));
return ab;
}
catch (ClassCastException ex) {
ArrayList decisionVoters = new ArrayList();
ResourceBasedPreInvocationAdvice expressionAdvice = new ResourceBasedPreInvocationAdvice();
decisionVoters.add(new PreInvocationAuthorizationAdviceVoter(expressionAdvice));
return new AffirmativeBased(decisionVoters);
}
}
public class AclAuthenticationProvider implements AuthenticationProvider
{
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException
{
return null;
}
#Override
public boolean supports(Class<?> authentication)
{
return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
}
}
public class SessionInitializer extends AbstractHttpSessionApplicationInitializer
{
public SessionInitializer()
{
super(SecurityConfig.class);
}
}
}
And finally where I face the problem:
public class ResourceBasedPreInvocationAdvice implements PreInvocationAuthorizationAdvice
{
#Autowired
private User currentUser;
#Autowired
private AclEngine aclEngine;
#Override
public boolean before(Authentication authentication, MethodInvocation methodInvocation, PreInvocationAttribute preInvocationAttribute)
{
//Where I want to access currentUser and aclEngine but they are null.
//I can trace the code to this point without any Exception thrown!
}
}
#Override
protected AccessDecisionManager accessDecisionManager()
{
try {
AffirmativeBased ab = (AffirmativeBased) super.accessDecisionManager();
List<AccessDecisionVoter<? extends Object>> advs = ab.getDecisionVoters();
ResourceBasedPreInvocationAdvice expressionAdvice = new ResourceBasedPreInvocationAdvice();
List<AccessDecisionVoter<? extends Object>> toBeRemoved = new ArrayList<>();
for (AccessDecisionVoter<? extends Object> adv : advs) {
if (adv instanceof PreInvocationAuthorizationAdviceVoter) {
toBeRemoved.add(adv);
}
}
for (AccessDecisionVoter<? extends Object> adv : toBeRemoved) {
advs.remove(adv);
}
advs.add(new PreInvocationAuthorizationAdviceVoter(expressionAdvice));
return ab;
}
catch (ClassCastException ex) {
ArrayList decisionVoters = new ArrayList();
ResourceBasedPreInvocationAdvice expressionAdvice = new ResourceBasedPreInvocationAdvice();
decisionVoters.add(new PreInvocationAuthorizationAdviceVoter(expressionAdvice));
return new AffirmativeBased(decisionVoters);
}
}
Spring will only inject references into class instances (aka beans) that it manages. When you are creating beans inside methods and directly inject them into other beans, those newly created beans are Spring Managed beans and as such aren't eligible for any auto wiring or post processing by spring whatsoever.
Instead of
ResourceBasedPreInvocationAdvice expressionAdvice = new ResourceBasedPreInvocationAdvice();
You should move that code to a #Bean method so that is becomes a Spring managed bean and will be injected with the dependencies.
#Bean
public ResourceBasedPreInvocationAdvice expressionAdvice() {
return new ResourceBasedPreInvocationAdvice();
}
And just reference this method instead of creating a new instance.
ResourceBasedPreInvocationAdvice expressionAdvice = expressionAdvice();
I am trying to implement Method Security using #PreAuthorize.
Spring Version: 4.2.3.Release
Spring Security: 4.0.3.Release
I have implemented a CustomPermissionEvaluator. I have noticed that it seems to be working fine except for 1 service where the hasPmerission is not called.
I know this because I get the a logging message from hasPermission / or in the erroneous case do not get the log:
public boolean hasPermission(Authentication authentication, Object o, Object o1) {
logger.info("Call to hasPermission with "+o+" and "+o1);
...
}
My Spring configuration is as follows:
#Configuration
#ComponentScan
public class RootConfiguration {
}
MVC Config
#EnableWebMvc
#Configuration
#ComponentScan({"OntoRais.*"})
#PropertySource("classpath:application.properties")
#EnableGlobalMethodSecurity(prePostEnabled=true)
public class MvcConfiguration extends WebMvcConfigurerAdapter{
#Bean
public ViewResolver getViewResolver(){
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
return resolver;
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
registry.addResourceHandler("swagger-ui.html")
.addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
}
#Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigIn() {
return new PropertySourcesPlaceholderConfigurer();
}
#Bean(name="multipartResolver")
public CommonsMultipartResolver commonsMultipartResolver(){
CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver();
commonsMultipartResolver.setDefaultEncoding("utf-8");
commonsMultipartResolver.setMaxUploadSize(50000000);
return commonsMultipartResolver;
}
}
Method Security Config:
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
#ComponentScan
public class MethodSecurityConfiguration extends GlobalMethodSecurityConfiguration {
#Autowired
private CustomPermissionEvaluator permissionEvaluator;
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
DefaultMethodSecurityExpressionHandler handler
= new DefaultMethodSecurityExpressionHandler();
handler.setPermissionEvaluator(permissionEvaluator);
return handler;
}
public CustomPermissionEvaluator getPermissionEvaluator() {
return permissionEvaluator;
}
public void setPermissionEvaluator(CustomPermissionEvaluator permissionEvaluator) {
this.permissionEvaluator = permissionEvaluator;
}
}
Initializer:
#Configuration
#EnableSpringConfigured
public class MessageWebApplicationInitializer extends
AbstractAnnotationConfigDispatcherServletInitializer {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
servletContext.addListener(org.springframework.web.context.request.RequestContextListener.class);
super.onStartup(servletContext);
}
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { MvcConfiguration.class };
}
#Override
protected Class<?>[] getServletConfigClasses() {
return null;
}
#Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
#Override
protected Filter[] getServletFilters() {
return new Filter[]{new HiddenHttpMethodFilter(),
new OpenEntityManagerInViewFilter(),
new DelegatingFilterProxy("springSecurityFilterChain")
};
}
}
Security Config:
#Configuration
#EnableWebSecurity
#ComponentScan
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
OntoRAISUserDetailsService ontoRAISUserDetailsService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.
formLogin().
and().
logout().
and().
authorizeRequests().
antMatchers("/login").permitAll().
anyRequest().authenticated().
and().csrf().disable();
}
#Autowired
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(ontoRAISUserDetailsService);
auth.authenticationProvider(authenticationProvider());
}
#Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setUserDetailsService(ontoRAISUserDetailsService);
authenticationProvider.setPasswordEncoder(passwordEncoder());
return authenticationProvider;
}
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
public OntoRAISUserDetailsService getOntoRAISUserDetailsService() {
return ontoRAISUserDetailsService;
}
public void setOntoRAISUserDetailsService(OntoRAISUserDetailsService ontoRAISUserDetailsService) {
this.ontoRAISUserDetailsService = ontoRAISUserDetailsService;
}
The Service in question:
#Service
public class StakeholderService {
#Autowired
private OntopManager om;
private static final Logger logger = LoggerFactory.getLogger("OntoRais");
public OntopManager getOm() {
return om;
}
public void setOm(OntopManager om) {
this.om = om;
}
#PreAuthorize("hasPermission(#stakeholderType, 'Create_StakeholderType')")
public void createStakeholderType(StakeholderType stakeholderType) {
try {
logger.info("Create stakeholder type in service layer");
List<OBDADataSource> sources = om.getObdaModel().getSources();
OBDAMappingAxiom mapping = om.getObdaModel().getMapping(new URI("genertatedURI"), MappingList.StakheholderType());
HashMap<String, String> values = new HashMap<>();
values.put("stakeholderName", stakeholderType.getLabel());
String query = ClassSQLHelper.generateSQLCreateSatement(mapping.getSourceQuery(), values);
SQLHelper.executeSQL(query, sources.get(0));
} catch (URISyntaxException e) {
logger.error(e.getMessage());
}
}
And the controller from which i call the service layer:
#Api(description = "Operations related to Stakeholders")
#RestController
public class StakeholderController {
#Autowired
private OntopManager om;
#Autowired
StakeholderService stakeholderService;
#Autowired
ProjectService projectService;
private static final Logger logger = LoggerFactory.getLogger("OntoRais");
...
/**
* Add a new Stakeholder Type
*
* #param stakeholdertype The new Stakeholder to be added.
* #return
*/
#ApiOperation(value = "Add new stakeholder type",
notes = "",
response = ResponseResource.class,
responseContainer = "Object")
#JsonView(Views.Details.class)
#RequestMapping(value = "/api/stakeholder/types", method = RequestMethod.POST)
public ResponseEntity<List<StakeholderType>> addStakeholderType(#RequestBody StakeholderType stakeholdertype) {
logger.info("Add Stakeholder type in controller");
getStakeholderService().createStakeholderType(stakeholdertype);
return getStakeholderTypes();
}
When calling api/stakeholder/types" with method = POST
This is my debug output:
Add Stakeholder type in controller
Create stakeholder type in service layer
INSERT INTO prefix_http_www_ontorais_de_stakeholdertype(id,stakeholderName) VALUES(DEFAULT,'TESTEWRTERETE');
As you can see the log from hasPermission is not present -> not called.
I can see that the method is called from my other method sercurity annotations in other service objects.
A similar Service which correctly invokes hasPermission as expected just for comparison:
#Service
public class OrganisationService {
private static final Logger logger = LoggerFactory.getLogger("OntoRais");
#Autowired
private OntopManager om;
#Autowired
private ProjectService projectService;
...
#PreAuthorize("hasAuthority('Add_Organisation')")
public void addOrganisation(Organisation organisation) {
List<OBDADataSource> sources = om.getObdaModel().getSources();
OBDAMappingAxiom mapping = null;
try {
mapping = om.getObdaModel().getMapping(new URI("genertatedURI"), MappingList.OrganisationMapping());
} catch (URISyntaxException e) {
e.printStackTrace();
}
HashMap<String, String> valueMap = new HashMap<>();
valueMap.put("organisationName", organisation.getName());
valueMap.put("organisationDescription", organisation.getDescription());
String query = ClassSQLHelper.generateSQLCreateSatement(mapping.getSourceQuery(), valueMap);
SQLHelper.executeSQL(query, sources.get(0));
}
Any hints on what I am doing wrong/missing/am blind for are very welcome thanks.
Benedict
Ok I found the problem, and a solution.
The Problem was that my CustomPermissionEvaluator depended on a method within the StakeholderService. Even though that method was not secured this resulted in the Spring not being able to proxy the object, therefore preventing any security checks.
Even though it is a bad idea to use a service layer object in the PermissionEvaluator, perhaps someone could elaborate on the exact implications, as i am definately not an expert in spring sercurity
Say I have some application-wide configuration:
#Configuration
public class RestTemplateConfiguration {
#Bean
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public HttpClient anHttpClient() {
PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager();
// set some properties
// ...
return HttpClientBuilder.create().setConnectionManager(poolingHttpClientConnectionManager).build();
}
#Bean
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public ClientHttpRequestInterceptor aRequestInterceptor() {
....
}
#Bean
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public ClientHttpRequestInterceptor anotherRequestInterceptor() {
....
}
}
Then I have some particular service that looks like:
#Service
public class MyService {
private final RestTemplate myParticularRestTemplate;
#Autowired
public MyService(RestTemplate myParticularRestTemplate) {
this.myParticularRestTemplate = myParticularRestTemplate;
}
/***
* Some incredible application logic
***/
#Configuration
public static class Config {
private int SOME_READ_TIMEOUT;
private int SOME_CONNECT_TIMEOUT;
#Bean
public RestTemplate myParticularRestTemplate(HttpClient anHttpClient, List<ClientHttpRequestInterceptor> interceptors) {
HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(anHttpClient);
clientHttpRequestFactory.setReadTimeout(SOME_READ_TIMEOUT);
clientHttpRequestFactory.setConnectTimeout(SOME_CONNECT_TIMEOUT);
RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory);
restTemplate.setInterceptors(interceptors);
return restTemplate;
}
}
}
I want the interceptors to be injected into RestTemplates system-wide, but, as it is, because I've got to access nested objects of the RestTemplate to set particular configuration, I need to instantiate the RestTemplate myself -- at least as I understand it.
Is there a nicer way to address this that doesn't require configuration for specific RestTemplates to know about the List<ClientHttpRequestInterceptor>?
I'm facing some issues when I run and hit my rest service, I saw that my service instance got a #Proxy value. Below you can see the classes that I have:
Initializer class:
public class AllureWebInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
createDispatcherServlet(servletContext);
initializeListener(servletContext);
}
private void initializeListener(ServletContext servletContext) {
AnnotationConfigWebApplicationContext rootContext = createContext();
servletContext.addListener(new ContextLoaderListener(rootContext));
}
private void createDispatcherServlet(final ServletContext context) {
AnnotationConfigWebApplicationContext dispatcherServlet = new AnnotationConfigWebApplicationContext();
dispatcherServlet.register(AllureWebConfig.class);
ServletRegistration.Dynamic dispatcher = context.addServlet(AllureWebConstants.DISPATCHER, new DispatcherServlet(dispatcherServlet));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping(AllureWebConstants.SLASH);
}
private AnnotationConfigWebApplicationContext createContext() {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(AllureWebConfig.class);
return context;
}
}
Config class:
#Configuration
#Import(AllureServiceConfig.class)
#ComponentScan(basePackages = {"com.allure.events.web.controller"})
#EnableWebMvc
public class AllureWebConfig extends WebMvcConfigurerAdapter {
#Autowired
private ApplicationContextProvider applicationContextProvider;
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler(AllureWebConstants.RESOURCES_DOUBLE_WILDCARD)
.addResourceLocations(AllureWebConstants.RESOURCES);
}
#Bean
public ApplicationContextProvider applicationContextProvider() {
return applicationContextProvider;
}
#Bean
public MessageSource messageSource() {
ReloadableResourceBundleMessageSource messageSource;
messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename("classpath:META-INF/web/resources/release");
messageSource.setUseCodeAsDefaultMessage(true);
return messageSource;
}
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
final MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
final ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setSerializationInclusion(Include.NON_NULL);
converter.setObjectMapper(objectMapper);
converters.add(converter);
super.configureMessageConverters(converters);
}
}
Controller class:
#Controller
public class SupplierEndpoint extends AllureEndpoint {
#Autowired
SupplierService supplierService;
#RequestMapping(value = AllureWebConstants.SUPPLIERS, method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public List<Supplier> getSuppliers() {
List<Supplier> suppliers = getSuppliersList(supplierService.getSuppliers());
return suppliers;
}
}
Service interface
public interface SupplierService {
public List<SupplierDto> getSuppliers();
}
Service Impl
#Service
public class SupplierServiceImpl implements SupplierService {
#Autowired
private SupplierMapper supplierMapper;
#Autowired
private SupplierDtoHelper supplierHelper;
#Override
#Transactional
public List<SupplierDto> getSuppliers() {
List<Supplier> suppliers = supplierMapper.getSuppliers();
List<SupplierDto> dtos = new ArrayList<SupplierDto>();
for (Supplier supplier : suppliers) {
dtos.add(supplierHelper.convertEntityToDto(supplier));
}
return dtos;
}
}
if I debug, I see that the instance value is :
supplierService - $Proxy28
|
-> h = JdkDynamicAopProxy
Can someone guide me, please?
Regards,
Edgardo Quiroz
It's not an issue when you work with Spring.
Spring Framework uses Proxy to make the different features possible.
Here you have a Transactional Annotation and proxy is been used to implement it.