Replace RequestResponseBodyMethodProcessor with CustomMethodProcessor using BeanPostProcessor - spring

How can I swap RequestResponseBodyMethodProcessor with CustomRequestResponseBodyMethodProcessor in the BeanPostProcessor postProcessAfterInitialization() method?
I have copied entire code from RequestResponseBodyMethodProcessor and made some modification in my CustomRequestResponseBodyMethodProcessor.
Now I want Spring to use my CustomRequestResponseBodyMethodProcessor, not the inbuilt.
So tried overwriting in postProcessAfterInitialization() by implementing BeanPostProcessor.
In the below forum, where it says "create a new list of it, replace the normal RequestResponseBodyMethodProcessor with your custom implementation", how can I get handle to do this?
For Reference:
http://forum.spring.io/forum/spring-projects/web/130803-how-to-extend-requestresponsebodymethodprocessor-and-configure-it-in-webmvc-config-xm
Pseudo Code:
class BaseInsert {
commonattribute1;
commonattribute1;
}
class ChildInsert extends BaseInsert {
childattribute1;
childattribute2;
}
#PostMapping("/abc")
public Resource<?> insert(#RequestBody BaseInsert baseInsert){
...
}
I changed the code in CustomRequestResponseBodyMethodProcessor to assign ChildInsert in BaseInsert.

Solution 1: I will recommend this solution the most
#Configuration
#EnableWebMvc
public class AdapterConfig extends WebMvcConfigurerAdapter {
private final ApplicationContext applicationContext;
#Autowired
public TrackingAdapterConfig(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver>reso) {
super.addArgumentResolvers(reso);
reso.add( new CustomRequestBodyMethodProcessor(); }
}
public class CustomProcessor extends RequestResponseBodyMethodProcessor {
#Override
public boolean supportsParameter(MethodParameter parameter) {
return (parameter.getNestedGenericParameterType().getTypeName()
.equalsIgnoreCase(BaseInsert.class.getName()));
}
#Override protected <T> Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter parameter,Type paramType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
BaseInsert request = childInsert;
return super.readWithMessageConverters(webRequest, parameter, request.getClass());
}
}
Solution 2: This is also good solution but less performant because BeanPostProcessor interface has 2 methods 'postProcessBeforeInitialization()' and 'postProcessAfterInitialization()'.
So when you provide your implementation of this BeanPostProcessor interface with the class annotated as '#Configuration'.
postProcessBeforeInitialization() - This method is called every time before beans are created
postProcessAfterInitialization() - This method is called every time after beans are created.This is the place where CustomResolver can be added to list of resolvers
#Configuration
public class TestBeanPostProcessor implements BeanPostProcessor {
#Override
public Object postProcessBeforeInitialization(Object o, String s) throws BeansException {
return o;
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (beanName.equalsIgnoreCase("requestMappingHandlerAdapter")) {
RequestMappingHandlerAdapter requestMappingHandlerAdapter = (RequestMappingHandlerAdapter) bean;
List<HandlerMethodArgumentResolver> argumentResolvers = requestMappingHandlerAdapter.getArgumentResolvers();
List<HandlerMethodArgumentResolver> modifiedArgumentResolvers = new ArrayList<>(argumentResolvers.size());
for(int i =1; i< argumentResolvers.size();i++){
modifiedArgumentResolvers.add(argumentResolvers.get(i));
}
modifiedArgumentResolvers.add(new TestRequestBodyMethodProcessor(requestMappingHandlerAdapter.getMessageConverters(), new ArrayList<Object>()));
((RequestMappingHandlerAdapter) bean).setArgumentResolvers(null);
((RequestMappingHandlerAdapter) bean).setArgumentResolvers(modifiedArgumentResolvers);
}
return bean;
}
}
public class TestRequestBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {
public TestRequestBodyMethodProcessor(List<HttpMessageConverter<?>> converters) {
super(converters);
}
public TestRequestBodyMethodProcessor(List<HttpMessageConverter<?>> converters, ContentNegotiationManager manager) {
super(converters, manager);
}
public TestRequestBodyMethodProcessor(List<HttpMessageConverter<?>> converters, List<Object> requestResponseBodyAdvice) {
super(converters, null, requestResponseBodyAdvice);
}
public TestRequestBodyMethodProcessor(List<HttpMessageConverter<?>> converters,
ContentNegotiationManager manager, List<Object> requestResponseBodyAdvice) {
super(converters, manager, requestResponseBodyAdvice);
}
#Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(RequestBody.class);
}
#Override
public boolean supportsReturnType(MethodParameter returnType) {
return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
returnType.hasMethodAnnotation(ResponseBody.class));
}
#Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
parameter = parameter.nestedIfOptional();
BaseInsert trans_type_code = ;
Object arg = readWithMessageConverters(webRequest, parameter,
Test.getModelClassObject().getClass());
String name = Conventions.getVariableNameForParameter(parameter);
WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
if (arg != null) {
validateIfApplicable(binder, parameter);
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
}
}
mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
return adaptArgumentIfNecessary(arg, parameter);
}
#Override
protected <T> Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter parameter,
Type paramType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(servletRequest);
Object arg = readWithMessageConverters(inputMessage, parameter, paramType);
if (arg == null) {
if (checkRequired(parameter)) {
throw new HttpMessageNotReadableException("Required request body is missing: " +
parameter.getMethod().toGenericString());
}
}
return arg;
}
protected boolean checkRequired(MethodParameter parameter) {
return (parameter.getParameterAnnotation(RequestBody.class).required() && !parameter.isOptional());
}
#Override
public void handleReturnValue(Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
mavContainer.setRequestHandled(true);
ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
// Try even with null return value. ResponseBodyAdvice could get involved.
writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}
}

I tried the Solution 1 from previous post but also need this:
#Autowired
private RequestMappingHandlerAdapter adapter;
#PostConstruct
public void prioritizeCustomArgumentMethodHandlers () {
List<HandlerMethodArgumentResolver> argumentResolvers = new ArrayList<>(adapter.getArgumentResolvers ());
List<HandlerMethodArgumentResolver> customResolvers = adapter.getCustomArgumentResolvers();
argumentResolvers.removeAll(customResolvers);
argumentResolvers.addAll (0, customResolvers);
adapter.setArgumentResolvers (argumentResolvers);
}
Without this code, program doesn´t stop at my custom RequestResponseBodyMethodProcessor.
You can check my post : Override default message when #ResponseBody is null

Related

Spring Batch MultiResourceItemReader re-reading first resource

I am writing a Spring Batch application using Spring Boot 1.5, following are my classes : -
CustomMultiResourceItemReader.java
#StepScoped
#Component
public class CustomMultiResourceItemReader
extends MultiResourceItemReader<MyDTO> {
public MultiResourceXmlItemReader(
#NonNull final MyResourceAwareItemReader itemReader,
#NonNull final ApplicationContext ctx)
throws IOException {
setResources(
ctx.getResources(
String.format(
"file:%s/*.xml", "~/data"))); // gives me a Resource[] array fine
setDelegate(itemReader);
}
#PreDestroy
void destroy() {
close();
}
}
MyResourceAwareItemReader.java
#RequiredArgsConstructor
#StepScope
#Component
#Slf4j
public class MyResourceAwareItemReader
implements ResourceAwareItemReaderItemStream<MyDTO> {
private static final String RESOURCE_NAME_KEY = "RESOURCE_NAME_KEY";
#NonNull private final Unmarshaller unmarshaller; // JaxB Unmarshaller
private Resource resource;
#Override
public void setResource(Resource resource) {
this.resource = resource; // **gets called only once**
}
#Override
public MyDTO read() throws Exception {
final MyDTO dto = (MyDTO) unmarshaller.unmarshal(resource.getFile()); // Standard JaxB unmarshalling.
return dto;
}
#Override
public void open(ExecutionContext executionContext) throws ItemStreamException {
if (executionContext.containsKey(RESOURCE_NAME_KEY)) {
} else if (resource != null) {
executionContext.put(RESOURCE_NAME_KEY, resource.getFilename());
}
}
#Override
public void update(ExecutionContext executionContext) throws ItemStreamException {
if (resource != null) executionContext.put(RESOURCE_NAME_KEY, resource.getFilename());
}
#Override
public void close() throws ItemStreamException {}
}
The problem is the setResource method in the delegate reader (MyResourceAwareItemReader.java) gets called only once at the beginning; while the read method gets called multiple times, as a result I read the same item multiple times, instead of reading the next item as expected.
I have also browsed the source code of MultiResouceItemReader in Spring Batch, it seems like, the read method of the delegate class is supposed to return null after each item is read, I can clearly see my code doesnt seem to do that.
I am bit lost how to make this work. Any help is much appreciated
Looking further into it, ItemReader documentation, clearly details that reader must return null at the end of the input data set. So basically I implemented my ItemReader with a boolean flag as follows: -
#RequiredArgsConstructor
#StepScope
#Component
#Slf4j
public class MyResourceAwareItemReader
implements ResourceAwareItemReaderItemStream<MyDTO> {
private static final String RESOURCE_NAME_KEY = "RESOURCE_NAME_KEY";
#NonNull private final Unmarshaller unmarshaller; // JaxB Unmarshaller
private Resource resource;
private boolean isResourceRead;
#Override
public void setResource(Resource resource) {
this.resource = resource;
isResourceRead = false;
}
#Override
public MyDTO read() throws Exception {
if(isResourceRead == true) return null;
final MyDTO dto = (MyDTO) unmarshaller.unmarshal(resource.getFile());
isResourceRead = true;
return dto;
}
#Override
public void open(ExecutionContext executionContext) throws ItemStreamException {
if (executionContext.containsKey(RESOURCE_NAME_KEY)) {
} else if (resource != null) {
executionContext.put(RESOURCE_NAME_KEY, resource.getFilename());
}
}
#Override
public void update(ExecutionContext executionContext) throws ItemStreamException {
if (resource != null) executionContext.put(RESOURCE_NAME_KEY, resource.getFilename());
}
#Override
public void close() throws ItemStreamException {}
}
MultiResourceItemReader does not returns null each time. If there are no more resources to read the it returns NULL otherwise it returns the next resources to the delegate that means – Your actual reader
I can see problem in your read() method . you are not moving to next file. As you are implementing you own MultiResourceItemReader It’s your responsibility to move to next resources item.
This is how it is implanted in MultiResourceItemReader . You will need your own similar implementation.
private T readNextItem() throws Exception {
T item = delegate.read();
while (item == null) {
currentResource++;
if (currentResource >= resources.length) {
return null;
}
delegate.close();
delegate.setResource(resources[currentResource]);
delegate.open(new ExecutionContext());
item = delegate.read();
}
return item;
}
You need to maintain index of resources array . Please check implementation of MultiResourceItemReader. You need to do exactly similar way

How can I moderate #PathVariable?

I have following methods.
public ResponseEntity some(#PathVariable("id") final String id_) {
final Long id = ((Supplier<Long>) () -> {
try {
if (id_ == null || id_.equals("dontcare")) {
return null;
}
return Long.parseLong(id_);
} catch (final NumberFormatException nfe) {
throw new RuntimeException(nfe);
}
}).get();
}
As you can assume I want my parameter value \d+ or dontcare.
How or What can I use for do following?
public ResponseEntity some(
#Dontcare #PathVariable("id") final Long id) {
}
I found WebArgumentHandler, HandlerMethodArgumentResolver, and PathVariableMethodArgumentResolver, but I can't find any good example.
Please help me.
UPDATE
I prepared following class.
public class DontcarePathVariableResolver
extends PathVariableMethodArgumentResolver {
#Override
public boolean supportsParameter(MethodParameter parameter) {
final boolean flag = parameter.getMethodAnnotation(
DontcarePathVariable.class) != null;
logger.info("flag: {}", flag);
return flag;
}
#Override
protected Object resolveName(String name, MethodParameter parameter,
NativeWebRequest request)
throws Exception {
#SuppressWarnings({"unchecked"})
final Map<String, String> templateVariables
= (Map<String, String>) request.getAttribute(
HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE,
RequestAttributes.SCOPE_REQUEST);
final String pathValue = templateVariables.get(name);
return new DontcareConverter().convert(pathValue);
}
}
And added it.
#Configuration
public class WebMvcConfiguration extends WebMvcConfigurerAdapter {
#Override
public void addArgumentResolvers(final List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(new DontcarePathVariableMethodArgumentResolver());
logger.info("resolver added");
//super.addArgumentResolvers(resolvers);
}
}
But it did come to play.
I think a default handler for Long type is already in play.

Spring Security: How do I enable custom expression result type support?

In my Spring Boot application I'm using the #PreAuthorize annotation in my controller methods to make them authorized. The expressions use simple boolean-returning methods, like this:
#ResponseStatus(OK)
#PreAuthorize("#auth.authentication.mayReadMe(principal)")
public UserDto readMe() {
...
The mayReadMe(...) method simply returns a boolean value, however it uses ternary logic under the hood and just converts a special enum to boolean:
boolean mayReadMe(#Nonnull UserDetails principal);
Now let's say I want to rework the authorization components and let the method return the enum:
#Nonnull
foo.bar.FooBarEnum mayReadMe(#Nonnull final UserDetails principal);
However, I'm getting the following exception:
java.lang.IllegalArgumentException: Failed to evaluate expression '#primaryAuth.authentication.mayReadMe(principal)'
at org.springframework.security.access.expression.ExpressionUtils.evaluateAsBoolean(ExpressionUtils.java:15)
at org.springframework.security.access.expression.method.ExpressionBasedPreInvocationAdvice.before(ExpressionBasedPreInvocationAdvice.java:44)
at org.springframework.security.access.prepost.PreInvocationAuthorizationAdviceVoter.vote(PreInvocationAuthorizationAdviceVoter.java:57)
at org.springframework.security.access.prepost.PreInvocationAuthorizationAdviceVoter.vote(PreInvocationAuthorizationAdviceVoter.java:25)
at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:62)
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:232)
at org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor.invoke(MethodSecurityInterceptor.java:64)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:655)
...
Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1001E:(pos 0): Type conversion problem, cannot convert from #javax.annotation.Nonnull foo.bar.FooBarEnum to java.lang.Boolean
at org.springframework.expression.spel.support.StandardTypeConverter.convertValue(StandardTypeConverter.java:78)
at org.springframework.expression.common.ExpressionUtils.convertTypedValue(ExpressionUtils.java:53)
at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.java:301)
at org.springframework.security.access.expression.ExpressionUtils.evaluateAsBoolean(ExpressionUtils.java:11)
... 113 common frames omitted
Caused by: org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [#javax.annotation.Nonnull foo.bar.FooBarEnum] to type [java.lang.Boolean]
at org.springframework.core.convert.support.GenericConversionService.handleConverterNotFound(GenericConversionService.java:313)
at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:195)
at org.springframework.expression.spel.support.StandardTypeConverter.convertValue(StandardTypeConverter.java:74)
... 116 common frames omitted
The exception message is really clear, but I can't inject my custom converter in any way. What I've tried so far:
Registering custom converters via WebMvcConfigurerAdapter.addFormatters(FormatterRegistry) (both Converter and GenericConverter)
Bean-ining a custom ExpressionBasedPreInvocationAdvice (but it shouldn't work as far as I understand)
... and a few other ways I can't recall after spending a few hours unfortunately.
How do I inject a custom type converter so the #PreAuthorization expressions could be aware of the foo.bar.FooBarEnum as the returning type?
Edit 1
Why do I need a custom type to be returned, and not a boolean. I'm also writing a simple REST API self-describing subsystem, just a simple GET /api endpoint to return a list of endpoints and so on. This list consists of a certain objects describing API end point, HTTP method, incoming and outgoing DTOs, and the last thing I'm trying to add to the definition object is an endpoint authorization policy expression. Note that it's not a good idea to return the #PreAuthorize string expression (I mean a raw string), but it might be good to return a custom object describing the authorization rules. What I want the most is returning an object like:
public final class AuthorizationExpression
implements BooleanSupplier {
...
public IExpression toExpression() {
...
}
where BooleanSupplier is expected to be used in the converter I'm trying to inject in order to satisfy the authorization needs -- just return true or false, and where IExpression is expected to be toString-ed in the GET /api handler using the Spring expression evaluator. Hence the mayReadMe signature might be as follows:
AuthorizationExpression mayReadMe(...)
so I could use AuthorizationExpression up to a certain use case. The FooBarEnum is just a simplification for the original question prior to the edit.
A suggestion, let your enum implement the conversion method:
public enum FooBarEnum {
// previous code
public boolean booleanValue() {
// TODO
}
}
And change your annotation:
#PreAuthorize("#auth.authentication.mayReadMe(principal).booleanValue()")
Figured it out. I only need to tune the DefaultMethodSecurityExpressionHandler instance. Let's assume the net two classes as library ones:
public abstract class CustomTypesGlobalMethodSecurityConfiguration
extends GlobalMethodSecurityConfiguration {
protected abstract ApplicationContext applicationContext();
protected abstract ConversionService conversionService();
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
final ApplicationContext applicationContext = applicationContext();
final TypeConverter typeConverter = new StandardTypeConverter(conversionService());
final DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler() {
#Override
public StandardEvaluationContext createEvaluationContextInternal(final Authentication authentication, final MethodInvocation methodInvocation) {
final StandardEvaluationContext decoratedStandardEvaluationContext = super.createEvaluationContextInternal(authentication, methodInvocation);
return new ForwardingStandardEvaluationContext() {
#Override
protected StandardEvaluationContext standardEvaluationContext() {
return decoratedStandardEvaluationContext;
}
#Override
public TypeConverter getTypeConverter() {
return typeConverter;
}
};
}
};
handler.setApplicationContext(applicationContext);
return handler;
}
}
where ForwardingStandardEvaluationContext is a simple forwarding decorator to decorate an instance of StandardEvaluationContext because the latter is ConversionService-aware:
public abstract class ForwardingStandardEvaluationContext
extends StandardEvaluationContext {
protected abstract StandardEvaluationContext standardEvaluationContext();
#Override public void setRootObject(final Object rootObject, final TypeDescriptor typeDescriptor) { standardEvaluationContext().setRootObject(rootObject, typeDescriptor); }
#Override public void setRootObject(final Object rootObject) { standardEvaluationContext().setRootObject(rootObject); }
#Override public TypedValue getRootObject() { return standardEvaluationContext().getRootObject(); }
#Override public void addConstructorResolver(final ConstructorResolver resolver) { standardEvaluationContext().addConstructorResolver(resolver); }
#Override public boolean removeConstructorResolver(final ConstructorResolver resolver) { return standardEvaluationContext().removeConstructorResolver(resolver); }
#Override public void setConstructorResolvers(final List<ConstructorResolver> constructorResolvers) { standardEvaluationContext().setConstructorResolvers(constructorResolvers); }
#Override public List<ConstructorResolver> getConstructorResolvers() { return standardEvaluationContext().getConstructorResolvers(); }
#Override public void addMethodResolver(final MethodResolver resolver) { standardEvaluationContext().addMethodResolver(resolver); }
#Override public boolean removeMethodResolver(final MethodResolver methodResolver) { return standardEvaluationContext().removeMethodResolver(methodResolver); }
#Override public void setMethodResolvers(final List<MethodResolver> methodResolvers) { standardEvaluationContext().setMethodResolvers(methodResolvers); }
#Override public List<MethodResolver> getMethodResolvers() { return standardEvaluationContext().getMethodResolvers(); }
#Override public void setBeanResolver(final BeanResolver beanResolver) { standardEvaluationContext().setBeanResolver(beanResolver); }
#Override public BeanResolver getBeanResolver() { return standardEvaluationContext().getBeanResolver(); }
#Override public void addPropertyAccessor(final PropertyAccessor accessor) { standardEvaluationContext().addPropertyAccessor(accessor); }
#Override public boolean removePropertyAccessor(final PropertyAccessor accessor) { return standardEvaluationContext().removePropertyAccessor(accessor); }
#Override public void setPropertyAccessors(final List<PropertyAccessor> propertyAccessors) { standardEvaluationContext().setPropertyAccessors(propertyAccessors); }
#Override public List<PropertyAccessor> getPropertyAccessors() { return standardEvaluationContext().getPropertyAccessors(); }
#Override public void setTypeLocator(final TypeLocator typeLocator) { standardEvaluationContext().setTypeLocator(typeLocator); }
#Override public TypeLocator getTypeLocator() { return standardEvaluationContext().getTypeLocator(); }
#Override public void setTypeConverter(final TypeConverter typeConverter) { standardEvaluationContext().setTypeConverter(typeConverter); }
#Override public TypeConverter getTypeConverter() { return standardEvaluationContext().getTypeConverter(); }
#Override public void setTypeComparator(final TypeComparator typeComparator) { standardEvaluationContext().setTypeComparator(typeComparator); }
#Override public TypeComparator getTypeComparator() { return standardEvaluationContext().getTypeComparator(); }
#Override public void setOperatorOverloader(final OperatorOverloader operatorOverloader) { standardEvaluationContext().setOperatorOverloader(operatorOverloader); }
#Override public OperatorOverloader getOperatorOverloader() { return standardEvaluationContext().getOperatorOverloader(); }
#Override public void setVariable(final String name, final Object value) { standardEvaluationContext().setVariable(name, value); }
#Override public void setVariables(final Map<String, Object> variables) { standardEvaluationContext().setVariables(variables); }
#Override public void registerFunction(final String name, final Method method) { standardEvaluationContext().registerFunction(name, method); }
#Override public Object lookupVariable(final String name) { return standardEvaluationContext().lookupVariable(name); }
#Override public void registerMethodFilter(final Class<?> type, final MethodFilter filter) throws IllegalStateException { standardEvaluationContext().registerMethodFilter(type, filter); }
}
And then a couple application classes:
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = false)
class SecurityConfiguration
extends CustomTypesGlobalMethodSecurityConfiguration {
private final ApplicationContext applicationContext;
private final ConversionService conversionService;
public SecurityConfiguration(
#Autowired final ApplicationContext applicationContext,
#Autowired final ConversionService conversionService
) {
this.applicationContext = applicationContext;
this.conversionService = conversionService;
}
#Override
protected ApplicationContext applicationContext() {
return applicationContext;
}
#Override
protected ConversionService conversionService() {
return conversionService;
}
}
And finally the conversion service configuration:
#Configuration
class ConversionConfiguration {
#Bean
public ConversionService conversionService() {
final DefaultConversionService conversionService = new DefaultConversionService();
conversionService.addConverter(FooBar.class, Boolean.class, FooBar::mayProceed);
return conversionService;
}
}
The code above makes #PreAuthorize to understand FooBar-returning expressions.

why BeanDefinition.getPropertyValues return an empty list

I configured spring with AnnotationConfigApplicationContext. While I called BeanDefinition.getPropertyValues() in a BeanFactoryPostProcessor implementation , I got an empty list . But if i configured spring with ClasspathXmlApplicationContext, it return the right value. Does anyone know the reason?
Here is my codes:
#Configuration
public class AppConfig {
#Bean
public BeanFactoryPostProcessorTest beanFactoryPostProcessorTest(){
return new BeanFactoryPostProcessorTest();
}
#Bean
public Person person(){
Person person = new Person();
person.setName("name");
person.setAge(18);
return person;
}
}
public class BeanFactoryPostProcessorTest implements BeanFactoryPostProcessor{
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
BeanDefinition bd = beanFactory.getBeanDefinition("person");
MutablePropertyValues propertyValues = bd.getPropertyValues();
if(propertyValues.contains("name")){
propertyValues.addPropertyValue("name","zhangfei");
}
System.out.println("Factory modfy zhangfei");
}
}
public class MainClass {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
/*ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("context.xml");*/
Person person = context.getBean(Person.class);
System.out.println(person.getName());
}
}
Thanks very much.
You have got a null list simply because the BeanFactoryPostProcessor is called before the instances are created and assigned values. They will be called before the instance creation.
I have done the same thing which you were trying to do using a different interface. Use the BeanPostProcessor interface.
public class BeanPostProcessorTest implements BeanPostProcessor{
#Override
public Object postProcessAfterInitialization(Object bean, String name)
throws BeansException {
if("person".equals(name)){
Person person =((Person)bean);
if("name".equals(person.getName())){
person.setName("zhangfei");
}
return person;
}
return bean;
}
#Override
public Object postProcessBeforeInitialization(Object arg0, String arg1)
throws BeansException {
return arg0;
}
}

Spring MVC with Atmosphere

I have recently started with Atmosphere. I need it to implement it in a Spring MVC application.
Till now I've managed to integrate it with Spring MVC.
I just need to perform a very simple task. I have a counter an instance variable as soon as it reaches 10, a response should be broadcasted to the UI.
Can anyone help me how do I write the code for that in the controller.
I've got the Atmosphere resource into the controller.
AtmosphereArgumentResolver.java
public class AtmosphereArgumentResolver implements HandlerMethodArgumentResolver {
//#Override
public boolean supportsParameter(MethodParameter parameter) {
return AtmosphereResource.class.isAssignableFrom(parameter.getParameterType());
}
//#Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception
{
HttpServletRequest httpServletRequest= webRequest.getNativeRequest(HttpServletRequest.class);
return Meteor.build(httpServletRequest).getAtmosphereResource();
}
}
HomeController.java
#Controller
public class HomeController {
private int counter = 0;
private final BroadcasterFactory bf;
public BroadcasterFactory broadcasterFactory()
{
return BroadcasterFactory.getDefault();
}
for(int i=0; i<=15; i++)
{
counter ++;
}
// As soon as the counter reaches 10 I need to send a broadcast message to the UI.
}
Can anyone please help? A skeleton code would also help as in which Atmosphere method to use for this?
I will copy/past the code i use in my application :
Controller :
#ManagedService(path = "/websocket/*")
#Singleton
public class LanesWebSocket {
private final Logger logger = LoggerFactory.getLogger(LanesWebSocket.class);
// private ScheduledExecutorService scheduledExecutorService;
private Future<?> scheduleFixedBroadcast;
private final ObjectMapper mapper = new ObjectMapper();
private SupervisionCenterService supervisionCenterService;
#Ready
public void onReady(final AtmosphereResource resource) {
if (this.supervisionCenterService == null)
supervisionCenterService = SpringApplicationContext.getBean(SupervisionCenterService.class);
Broadcaster bc = BroadcasterFactory.getDefault().lookup("lanes",true);
bc.addAtmosphereResource(resource);
scheduleFixedBroadcast = bc.scheduleFixedBroadcast(new Callable<String>() {
#Override
public String call() throws Exception {
try {
return mapper.writeValueAsString(supervisionCenterService.findCenterData());
} catch (Exception e) {
scheduleFixedBroadcast.cancel(true);
e.printStackTrace();
return null;
}
}
}, 1, TimeUnit.SECONDS);
}
And you also need to register the atmosphere servlet :
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
[...]
#Override
protected void registerDispatcherServlet(ServletContext servletContext) {
super.registerDispatcherServlet(servletContext);
initAtmosphereServlet(servletContext);
}
private void initAtmosphereServlet(ServletContext servletContext) {
AtmosphereServlet servlet = new AtmosphereServlet();
Field frameworkField = ReflectionUtils.findField(AtmosphereServlet.class, "framework");
ReflectionUtils.makeAccessible(frameworkField);
ReflectionUtils.setField(frameworkField, servlet, new NoAnalyticsAtmosphereFramework());
ServletRegistration.Dynamic atmosphereServlet =
servletContext.addServlet("atmosphereServlet", servlet);
atmosphereServlet.setInitParameter("org.atmosphere.cpr.packages", "com.myclient.theproduct.supervision.websocket");
atmosphereServlet.setInitParameter("org.atmosphere.cpr.broadcasterCacheClass", UUIDBroadcasterCache.class.getName());
atmosphereServlet.setInitParameter("org.atmosphere.cpr.broadcaster.shareableThreadPool", "true");
atmosphereServlet.setInitParameter("org.atmosphere.cpr.broadcaster.maxProcessingThreads", "10");
atmosphereServlet.setInitParameter("org.atmosphere.cpr.broadcaster.maxAsyncWriteThreads", "10");
servletContext.addListener(new org.atmosphere.cpr.SessionSupport());
atmosphereServlet.addMapping("/websocket/*");
atmosphereServlet.setLoadOnStartup(3);
atmosphereServlet.setAsyncSupported(true);
}
public class NoAnalyticsAtmosphereFramework extends AtmosphereFramework {
public NoAnalyticsAtmosphereFramework() {
super();
}
#Override
protected void analytics() {
// nothing
}
}
}
Don't ask me the reason of the NoAnalyticsAtmosphereFramework class, it could not work without.
Hope this will help you !

Resources