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.spring.boot.autoconfigure.ldaptive;
18  
19  import java.util.Objects;
20  import org.apache.commons.logging.Log;
21  import org.apache.commons.logging.LogFactory;
22  import org.bremersee.ldaptive.LdaptiveOperations;
23  import org.bremersee.ldaptive.LdaptiveProperties;
24  import org.bremersee.ldaptive.LdaptiveProperties.ConnectionPoolProperties;
25  import org.bremersee.ldaptive.LdaptiveTemplate;
26  import org.bremersee.ldaptive.converter.DnToStringConverter;
27  import org.bremersee.ldaptive.converter.StringToDnConverter;
28  import org.bremersee.ldaptive.reactive.ReactiveLdaptiveOperations;
29  import org.bremersee.ldaptive.reactive.ReactiveLdaptiveTemplate;
30  import org.ldaptive.ConnectionConfig;
31  import org.ldaptive.ConnectionFactory;
32  import org.ldaptive.DefaultConnectionFactory;
33  import org.ldaptive.PooledConnectionFactory;
34  import org.ldaptive.dn.Dn;
35  import org.ldaptive.pool.IdlePruneStrategy;
36  import org.mapstruct.Mapper;
37  import org.mapstruct.NullValueCheckStrategy;
38  import org.mapstruct.factory.Mappers;
39  import org.springframework.boot.autoconfigure.AutoConfiguration;
40  import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
41  import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
42  import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
43  import org.springframework.boot.context.event.ApplicationReadyEvent;
44  import org.springframework.boot.context.properties.EnableConfigurationProperties;
45  import org.springframework.context.annotation.Bean;
46  import org.springframework.context.event.EventListener;
47  import org.springframework.core.convert.converter.Converter;
48  import org.springframework.util.ClassUtils;
49  
50  /**
51   * The ldaptive configuration.
52   *
53   * @author Christian Bremer
54   */
55  @AutoConfiguration
56  @ConditionalOnClass(name = {
57      "org.ldaptive.ConnectionFactory",
58      "org.bremersee.ldaptive.LdaptiveProperties"
59  })
60  @ConditionalOnProperty(prefix = "bremersee.ldaptive", name = "ldap-url")
61  @EnableConfigurationProperties(LdaptiveConnectionProperties.class)
62  public class LdaptiveAutoConfiguration {
63  
64    private static final Log log = LogFactory.getLog(LdaptiveAutoConfiguration.class);
65  
66    private final Class<? extends LdaptiveTemplate> ldaptiveTemplateClass;
67  
68    private final LdaptiveProperties properties;
69  
70    /**
71     * Instantiates a new ldaptive configuration.
72     *
73     * @param properties the ldaptive connection properties
74     */
75    public LdaptiveAutoConfiguration(LdaptiveConnectionProperties properties) {
76      Class<?> cls = Objects
77          .requireNonNullElse(properties.getLdaptiveTemplateClass(), LdaptiveTemplate.class);
78      //noinspection unchecked
79      this.ldaptiveTemplateClass = LdaptiveTemplate.class.isAssignableFrom(cls)
80          ? (Class<? extends LdaptiveTemplate>) cls
81          : LdaptiveTemplate.class;
82      this.properties = PropertiesMapper.INSTANCE.map(properties);
83    }
84  
85    /**
86     * Init.
87     */
88    @EventListener(ApplicationReadyEvent.class)
89    public void init() {
90      log.info(String.format("""
91              
92              *********************************************************************************
93              * %s
94              * properties = %s
95              *********************************************************************************""",
96          ClassUtils.getUserClass(getClass()).getSimpleName(),
97          properties));
98    }
99  
100   /**
101    * Creates connection config.
102    *
103    * @return the connection config
104    */
105   @ConditionalOnMissingBean(ConnectionConfig.class)
106   @Bean
107   public ConnectionConfig connectionConfig() {
108     return properties.createConnectionConfig();
109   }
110 
111   /**
112    * Creates connection factory bean.
113    *
114    * @param connectionConfig the connection config
115    * @return the connection factory bean
116    */
117   @ConditionalOnMissingBean(ConnectionFactory.class)
118   @Bean(destroyMethod = "close")
119   public ConnectionFactory connectionFactory(ConnectionConfig connectionConfig) {
120 
121     if (properties.isPooled()) {
122       ConnectionPoolProperties props = this.properties.getConnectionPool();
123       PooledConnectionFactory factory = PooledConnectionFactory.builder()
124           .config(connectionConfig)
125           .blockWaitTime(props.getBlockWaitTime())
126           .connectOnCreate(props.isConnectOnCreate())
127           .failFastInitialize(props.isFailFastInitialize())
128           .max(props.getMaxPoolSize())
129           .min(props.getMinPoolSize())
130           .pruneStrategy(
131               new IdlePruneStrategy(props.getPrunePeriod(), props.getIdleTime()))
132           .validateOnCheckIn(props.isValidateOnCheckIn())
133           .validateOnCheckOut(props.isValidateOnCheckOut())
134           .validatePeriodically(props.isValidatePeriodically())
135           .validator(props.getValidator().createConnectionValidator())
136           .build();
137       factory.initialize();
138       return factory;
139     }
140     return new DefaultConnectionFactory(connectionConfig);
141   }
142 
143   /**
144    * Builds ldaptive template.
145    *
146    * @param connectionFactory the connection factory
147    * @return the ldaptive template
148    */
149   @ConditionalOnMissingBean(LdaptiveOperations.class)
150   @Bean
151   public LdaptiveTemplate ldaptiveTemplate(ConnectionFactory connectionFactory) {
152     try {
153       return ldaptiveTemplateClass
154           .getConstructor(ConnectionFactory.class)
155           .newInstance(connectionFactory);
156     } catch (Exception e) {
157       log.warn(String.format(
158           "Could not instantiate LdaptiveTemplate %s. Instantiate default template.",
159           ldaptiveTemplateClass.getName()), e);
160       return new LdaptiveTemplate(connectionFactory);
161     }
162   }
163 
164   /**
165    * Builds reactive ldaptive template.
166    *
167    * @param connectionFactory the connection factory
168    * @return the reactive ldaptive template
169    */
170   @ConditionalOnClass(name = {"reactor.core.publisher.Mono"})
171   @ConditionalOnMissingBean(ReactiveLdaptiveOperations.class)
172   @Bean
173   public ReactiveLdaptiveTemplate reactiveLdaptiveTemplate(ConnectionFactory connectionFactory) {
174     return new ReactiveLdaptiveTemplate(connectionFactory);
175   }
176 
177   /**
178    * String to dn converter.
179    *
180    * @return the converter
181    */
182   @ConditionalOnMissingBean
183   @Bean
184   public Converter<String, Dn> stringToDnConverter() {
185     return new StringToDnConverter();
186   }
187 
188   /**
189    * Dn to string converter.
190    *
191    * @return the converter
192    */
193   @ConditionalOnMissingBean
194   @Bean
195   public Converter<Dn, String> dnToStringConverter() {
196     return new DnToStringConverter();
197   }
198 
199   /**
200    * The properties-mapper.
201    */
202   @Mapper(nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS)
203   interface PropertiesMapper {
204 
205     /**
206      * The constant INSTANCE.
207      */
208     PropertiesMapper INSTANCE = Mappers.getMapper(PropertiesMapper.class);
209 
210     /**
211      * Map ldaptive properties.
212      *
213      * @param properties the properties
214      * @return the ldaptive properties
215      */
216     LdaptiveProperties map(LdaptiveConnectionProperties properties);
217   }
218 
219 }