Is there a way to integrate Springs #ExceptionHandler with Joinfaces - spring

i wanted to ask if there is a way to enable Springs #ExceptionHandler capabilities with Joinfaces/Primefaces.
For now i'm able to handle global #ControllerAdvice beans, but not if the #ExceptionHandler is inside the #Controller class.
Are there any suggestions on how to solve this topic?
Here is the code i wrote so far
#Slf4j
public class SpringJsfExceptionHandler extends ExceptionHandlerWrapper {
public SpringJsfExceptionHandler(ExceptionHandler wrapped) {
super(wrapped);
}
#Override
public void handle() throws FacesException {
final Iterator<ExceptionQueuedEvent> queue = getUnhandledExceptionQueuedEvents().iterator();
while (queue.hasNext()) {
ExceptionQueuedEvent item = queue.next();
ExceptionQueuedEventContext exceptionQueuedEventContext = (ExceptionQueuedEventContext) item.getSource();
try {
Throwable throwable = exceptionQueuedEventContext.getException();
FacesContext context = FacesContext.getCurrentInstance();
handleException(context, (Exception) throwable);
} finally {
queue.remove();
}
}
}
private void handleException(FacesContext context, Exception throwable) {
WebApplicationContext applicationContext = resolveApplicationContext(context);
Collection<HandlerExceptionResolver> exceptionResolvers = listExceptionHandlerResolvers(applicationContext);
for (HandlerExceptionResolver resolver : exceptionResolvers) {
resolver.resolveException(request(context), response(context), null, throwable);
}
}
private Collection<HandlerExceptionResolver> listExceptionHandlerResolvers(WebApplicationContext context) {
return context.getBeansOfType(HandlerExceptionResolver.class).values();
}
private HttpServletRequest request(FacesContext context) {
return (HttpServletRequest) context.getExternalContext().getRequest();
}
private HttpServletResponse response(FacesContext context) {
return (HttpServletResponse) context.getExternalContext().getResponse();
}
private WebApplicationContext resolveApplicationContext(FacesContext context) {
HttpServletRequest request = (HttpServletRequest) context.getExternalContext().getRequest();
return WebApplicationContextUtils.findWebApplicationContext(request.getServletContext());
}
}
public class SpringJsfExceptionHandlerFactory extends ExceptionHandlerFactory {
public SpringJsfExceptionHandlerFactory() {
}
public SpringJsfExceptionHandlerFactory(ExceptionHandlerFactory wrapped) {
super(wrapped);
}
#Override
public ExceptionHandler getExceptionHandler() {
return new SpringJsfExceptionHandler(getWrapped() != null ? getWrapped().getExceptionHandler() : null);
}
}
This works:
#ControllerAdvice
public class GlobalExceptionHandler {
#ExceptionHandler
public void handleCalculationException(CalculationException e) {
FacesContext.getCurrentInstance().
addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, e.getMessage(), e.getMessage()));
}
}
This does not work:
#Data
#Controller
#ViewScoped
public class CalculatorController implements Serializable {
#ExceptionHandler
public void handleCalculationException(CalculationException e) {
FacesContext.getCurrentInstance().
addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, e.getMessage(), e.getMessage()));
}
[...]
Thanks in advance

TLDR: No
#ExceptionHandler is part of Spring MVC.
Spring MVC and JSF are separate web frameworks.
Joinfaces allows you to use JSF in a Spring Application, and you can also use Spring MVC in the same application. Every request will however either be handled by Spring MVC (i.e. the DispatcherServlet) or JSF (i.e. the FacesServlet).

Related

Read custom header value from the response

When I send request from the Soap UI under raw response tab I see the following result(find attachment). Now in AOP controller I want to read this header value which is marked as red. How it is possible? Thanks in advance.
In my application to send soap requests I have WebServiceTemplate. I applied custom interceptor WebServiceInterceptor (which implements ClientInterceptor interface) on this web service template. In overridden afterCompletion method, which injects MessageContext, I was able to take this property from the SaajMessageHeader.
Here is what code looks like:
#Configuration
public class MyWebServiceConfig {
#Bean(name = "myWSClient")
public WebServiceTemplate myWSClient() throws Exception {
WebServiceTemplate template = new WebServiceTemplate();
...
WebServiceInterceptor[] interceptors = { new WebServiceInterceptor() };
template.setInterceptors(interceptors);
return template;
}
private static class WebServiceInterceptor implements ClientInterceptor {
#Override
public boolean handleRequest(MessageContext messageContext) throws WebServiceClientException {
...
return true;
}
#Override
public boolean handleResponse(MessageContext messageContext) throws WebServiceClientException {
return true;
}
#Override
public boolean handleFault(MessageContext messageContext) throws WebServiceClientException {
return true;
}
#Override
public void afterCompletion(MessageContext messageContext, Exception ex) throws WebServiceClientException {
try {
SaajSoapMessage message = (SaajSoapMessage) messageContext.getResponse();
String []traceId = message.getSaajMessage().getMimeHeaders().getHeader("ITRACING_TRACE_ID");
if(traceId != null && traceId.length > 0){
process.setTraceId(traceId[0]);
}
} catch (Exception e) {
}
}
}

Spring Webflux ErrorHandling - #RestControllerAdvice with #ExceptionHandler or DefaultErrorAttributes?

In Spring Webflux what is the prefered way of Exception Handling?
#RestControllerAdvice comes from Spring MVC whereas DefaultErrorAttributes comes from Spring Webflux.
However, in Spring Webflux someone could use #RestControllerAdvice. What would be the advantages/disadvantages?
#RestControllerAdvice
#RestControllerAdvice
public class ControllerAdvice
{
#ExceptionHandler(Throwable.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
public Mono<Map<String, Object>> exceptions(Throwable e)
{
return Mono.just(Map.of("message", "bad"));
}
}
Extend DefaultErrorAttributes
#Component
public class ErrorAttributes extends DefaultErrorAttributes
{
#Override
public Map<String, Object> getErrorAttributes(ServerRequest request, boolean includeStackTrace)
{
var ex = getError(request);
var attributes = new LinkedHashMap<String, Object>();
attributes.put("status", HttpStatus.BAD_REQUEST.value());
attributes.put("message", "bad");
return attributes;
}
}
I want to stay in the reactive world, so I tend more towards DefaultErrorAttributes (which plays well with DefaultErrorWebExceptionHandler in Webflux). However, in #RestControllerAdvice I could also use Mono.just(...).
It is same. Like WebMvc.
#RestControllerAdvice
public class ControllerAdvice {
#ExceptionHandler(AnyException.class)
public Mono<EntityResponse<YourModel>> example(AnyException exception) {
return EntityResponse.fromObject(new YourModel()).status(HttpStatus.NOT_FOUND).build();
}
}
In Spring Webflux in case functional routes declaration, you can also implement your own ExceptionHandler instead of DefaultErrorWebExceptionHandler:
class SystemErrorWebExceptionHandler extends AbstractErrorWebExceptionHandler {
#Override
public Mono<Void> handle(ServerWebExchange exchange, Throwable throwable) {
return super.handle(exchange, throwable)
// debug, process
.contextWrite(...);
}
#Override
protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
// for all routs
return route(all(), this::renderErrorResponse);
}
private Mono<ServerResponse> renderErrorResponse(ServerRequest request) {
Map<String, Object> error = getErrorAttributes(request, ErrorAttributeOptions.of());
Throwable t = this.getError(request);
// map exception on response
return ServerResponse.status(status).body(...);
}
}
Then use your implementation of AbstractErrorWebExceptionHandler in the spring configuration with #AutoConfigureBefore(WebFluxAutoConfiguration.class)

Replace RequestResponseBodyMethodProcessor with CustomMethodProcessor using BeanPostProcessor

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

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 !

How to access a Jersey bean from Spring FactoryBean?

I have a Spring BeanFactory and I need to inject httpHeaders to it.
public class SpecificFactoryBean extends SomeFactoryBean<Exception> {
#Context
private HttpHeaders httpHeaders;
protected SpecificFactoryBean() {
super(Exception.class);
}
#Override
protected void doRegistration(RegistryBuilder<Exception> registryBuilder) {
registryBuilder.registerMapper(WebApplicationException.class, new ExceptionMapper<WebApplicationException>() {
#Override
public int map(WebApplicationException e) {
httpHeaders.getSomeInfo();
return 3;
}
});
}
}
Now #Context doesn't work, so how can I do this?

Resources