View Javadoc
1   /*
2   * Copyright 2019-2026 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.bremersee.exception.spring.boot.autoconfigure.reactive;
18  
19  import org.apache.commons.logging.Log;
20  import org.apache.commons.logging.LogFactory;
21  import org.bremersee.exception.RestApiExceptionMapper;
22  import org.bremersee.exception.spring.boot.autoconfigure.RestApiExceptionMapperBootProperties;
23  import org.springframework.beans.factory.ObjectProvider;
24  import org.springframework.boot.autoconfigure.AutoConfiguration;
25  import org.springframework.boot.autoconfigure.AutoConfigureAfter;
26  import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
27  import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
28  import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
29  import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
30  import org.springframework.boot.autoconfigure.web.WebProperties;
31  import org.springframework.boot.context.event.ApplicationReadyEvent;
32  import org.springframework.boot.context.properties.EnableConfigurationProperties;
33  import org.springframework.boot.webflux.error.ErrorAttributes;
34  import org.springframework.context.ApplicationContext;
35  import org.springframework.context.annotation.Bean;
36  import org.springframework.context.event.EventListener;
37  import org.springframework.core.annotation.Order;
38  import org.springframework.http.codec.ServerCodecConfigurer;
39  import org.springframework.util.Assert;
40  import org.springframework.util.ClassUtils;
41  
42  /**
43   * The api exception handler autoconfiguration.
44   *
45   * @author Christian Bremer
46   */
47  @ConditionalOnWebApplication(type = Type.REACTIVE)
48  @ConditionalOnClass(name = {
49      "tools.jackson.databind.ObjectMapper",
50      "org.bremersee.exception.RestApiExceptionMapper"
51  })
52  @ConditionalOnBean({
53      ErrorAttributes.class,
54      WebProperties.class,
55      ServerCodecConfigurer.class
56  })
57  @AutoConfigureAfter({
58      RestApiExceptionMapperForWebFluxAutoConfiguration.class
59  })
60  @AutoConfiguration
61  @EnableConfigurationProperties({RestApiExceptionMapperBootProperties.class})
62  public class ApiExceptionHandlerAutoConfiguration {
63  
64    private static final Log log = LogFactory.getLog(ApiExceptionHandlerAutoConfiguration.class);
65  
66    private final RestApiExceptionMapperBootProperties properties;
67  
68    /**
69     * Instantiates a new api exception handler autoconfiguration.
70     *
71     * @param properties the properties
72     */
73    public ApiExceptionHandlerAutoConfiguration(
74        RestApiExceptionMapperBootProperties properties) {
75      this.properties = properties;
76    }
77  
78    /**
79     * Init.
80     */
81    @EventListener(ApplicationReadyEvent.class)
82    public void init() {
83      log.info(String.format("""
84              
85              *********************************************************************************
86              * %s
87              *********************************************************************************
88              * apiPaths = %s
89              *********************************************************************************""",
90          ClassUtils.getUserClass(getClass()).getSimpleName(),
91          properties.getApiPaths()));
92    }
93  
94    /**
95     * Builds api exception handler bean.
96     *
97     * @param errorAttributes the error attributes
98     * @param webProperties the web properties
99     * @param applicationContext the application context
100    * @param serverCodecConfigurer the server codec configurer
101    * @param restApiExceptionMapper the rest api exception mapper
102    * @return the api exception handler bean
103    */
104   @Bean
105   @Order(-2) // to have a higher priority than DefaultErrorWebExceptionHandler
106   public ApiExceptionHandler apiExceptionHandler(
107       ObjectProvider<ErrorAttributes> errorAttributes,
108       ObjectProvider<WebProperties> webProperties,
109       ApplicationContext applicationContext,
110       ObjectProvider<ServerCodecConfigurer> serverCodecConfigurer,
111       ObjectProvider<RestApiExceptionMapper> restApiExceptionMapper) {
112 
113     Assert.notNull(
114         errorAttributes.getIfAvailable(),
115         "Error attributes must be present.");
116     Assert.notNull(
117         serverCodecConfigurer.getIfAvailable(),
118         "Server codec configurer must be present.");
119     Assert.notNull(
120         restApiExceptionMapper.getIfAvailable(),
121         "Rest api exception mapper must be present.");
122 
123     return new ApiExceptionHandler(
124         properties.getApiPaths(),
125         errorAttributes.getIfAvailable(),
126         webProperties.getIfAvailable(WebProperties::new).getResources(),
127         applicationContext,
128         serverCodecConfigurer.getIfAvailable(),
129         restApiExceptionMapper.getIfAvailable());
130   }
131 
132 }