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