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.ldaptive;
18  
19  import java.util.Objects;
20  import java.util.Optional;
21  import lombok.extern.slf4j.Slf4j;
22  import org.bremersee.exception.ServiceException;
23  import org.ldaptive.AddOperation;
24  import org.ldaptive.AddRequest;
25  import org.ldaptive.BindOperation;
26  import org.ldaptive.BindRequest;
27  import org.ldaptive.BindResponse;
28  import org.ldaptive.CompareOperation;
29  import org.ldaptive.CompareRequest;
30  import org.ldaptive.CompareResponse;
31  import org.ldaptive.ConnectionFactory;
32  import org.ldaptive.DeleteOperation;
33  import org.ldaptive.DeleteRequest;
34  import org.ldaptive.LdapAttribute;
35  import org.ldaptive.LdapEntry;
36  import org.ldaptive.LdapException;
37  import org.ldaptive.ModifyDnOperation;
38  import org.ldaptive.ModifyDnRequest;
39  import org.ldaptive.ModifyOperation;
40  import org.ldaptive.ModifyRequest;
41  import org.ldaptive.Operation;
42  import org.ldaptive.Request;
43  import org.ldaptive.Result;
44  import org.ldaptive.ResultCode;
45  import org.ldaptive.SearchOperation;
46  import org.ldaptive.SearchRequest;
47  import org.ldaptive.SearchResponse;
48  import org.ldaptive.extended.ExtendedOperation;
49  import org.ldaptive.extended.ExtendedRequest;
50  import org.ldaptive.extended.ExtendedResponse;
51  import org.ldaptive.handler.ResultPredicate;
52  
53  /**
54   * The template for executing ldap operations.
55   *
56   * @author Christian Bremer
57   */
58  @Slf4j
59  public class LdaptiveTemplate implements LdaptiveOperations {
60  
61    private static final ResultPredicate NOT_COMPARE_RESULT = result -> !result.isSuccess()
62        && result.getResultCode() != ResultCode.COMPARE_TRUE
63        && result.getResultCode() != ResultCode.COMPARE_FALSE;
64  
65    private static final ResultPredicate NOT_DELETE_RESULT = result -> !result.isSuccess()
66        && result.getResultCode() != ResultCode.NO_SUCH_OBJECT;
67  
68    private static final ResultPredicate NOT_FIND_RESULT = NOT_DELETE_RESULT;
69  
70    private final ConnectionFactory connectionFactory;
71  
72    private LdaptiveErrorHandler errorHandler = new DefaultLdaptiveErrorHandler();
73  
74    /**
75     * Instantiates a new ldaptive template.
76     *
77     * @param connectionFactory the connection factory
78     */
79    public LdaptiveTemplate(ConnectionFactory connectionFactory) {
80      if (Objects.isNull(connectionFactory)) {
81        throw new IllegalArgumentException("Connection factory must not be present.");
82      }
83      this.connectionFactory = connectionFactory;
84    }
85  
86    @Override
87    public ConnectionFactory getConnectionFactory() {
88      return connectionFactory;
89    }
90  
91    /**
92     * Sets error handler.
93     *
94     * @param errorHandler the error handler
95     */
96    public void setErrorHandler(final LdaptiveErrorHandler errorHandler) {
97      if (errorHandler != null) {
98        this.errorHandler = errorHandler;
99      }
100   }
101 
102   @Override
103   public LdaptiveTemplate copy() {
104     return copy(null);
105   }
106 
107   @Override
108   public LdaptiveTemplate copy(LdaptiveErrorHandler errorHandler) {
109     final LdaptiveTemplate template = new LdaptiveTemplate(connectionFactory);
110     template.setErrorHandler(errorHandler);
111     return template;
112   }
113 
114   private <Q extends Request, S extends Result> S execute(Operation<Q, S> operation, Q request) {
115     try {
116       return operation.execute(request);
117 
118     } catch (Exception e) {
119       errorHandler.handleError(e);
120       return null;
121     }
122   }
123 
124   @Override
125   public void add(AddRequest request) {
126     execute(
127         AddOperation.builder()
128             .factory(getConnectionFactory())
129             .throwIf(ResultPredicate.NOT_SUCCESS)
130             .build(),
131         request);
132   }
133 
134   @Override
135   public boolean bind(BindRequest request) {
136     return Optional.ofNullable(execute(new BindOperation(getConnectionFactory()), request))
137         .map(BindResponse::isSuccess)
138         .orElse(false);
139   }
140 
141   @Override
142   public boolean compare(CompareRequest request) {
143     return Optional.ofNullable(execute(
144             CompareOperation.builder()
145                 .factory(getConnectionFactory())
146                 .throwIf(NOT_COMPARE_RESULT)
147                 .build(),
148             request))
149         .map(CompareResponse::isTrue)
150         .orElse(false);
151   }
152 
153   @Override
154   public void delete(DeleteRequest request) {
155     execute(
156         DeleteOperation.builder()
157             .factory(getConnectionFactory())
158             .throwIf(NOT_DELETE_RESULT)
159             .build(),
160         request);
161   }
162 
163   @Override
164   public ExtendedResponse executeExtension(ExtendedRequest request) {
165     return execute(
166         ExtendedOperation.builder()
167             .factory(getConnectionFactory())
168             .throwIf(ResultPredicate.NOT_SUCCESS)
169             .build(),
170         request);
171   }
172 
173   @Override
174   public void modify(ModifyRequest request) {
175     if (request.getModifications() != null && request.getModifications().length > 0) {
176       execute(
177           ModifyOperation.builder()
178               .factory(getConnectionFactory())
179               .throwIf(ResultPredicate.NOT_SUCCESS)
180               .build(),
181           request);
182     }
183   }
184 
185   @Override
186   public void modifyDn(ModifyDnRequest request) {
187     execute(
188         ModifyDnOperation.builder()
189             .factory(getConnectionFactory())
190             .throwIf(ResultPredicate.NOT_SUCCESS)
191             .build(),
192         request);
193   }
194 
195   @Override
196   public SearchResponse search(SearchRequest request) {
197     return execute(
198         SearchOperation.builder()
199             .factory(getConnectionFactory())
200             .throwIf(NOT_FIND_RESULT)
201             .build(),
202         request);
203   }
204 
205   @Override
206   public boolean exists(String dn) {
207     try {
208       SearchResponse response = SearchOperation.builder()
209           .factory(getConnectionFactory())
210           .throwIf(NOT_FIND_RESULT)
211           .build()
212           .execute(SearchRequest.objectScopeSearchRequest(dn));
213       return response.isSuccess();
214 
215     } catch (LdapException e) {
216       errorHandler.handleError(e);
217       return false;
218     }
219   }
220 
221   @Override
222   public <T> T save(T domainObject, LdaptiveEntryMapper<T> entryMapper) {
223 
224     String dn = entryMapper.mapDn(domainObject);
225     SearchResponse searchResponse = execute(
226         SearchOperation.builder()
227             .factory(getConnectionFactory())
228             .throwIf(result -> result.getResultCode() != ResultCode.NO_SUCH_OBJECT
229                 && result.getResultCode() != ResultCode.SUCCESS)
230             .build(),
231         SearchRequest.objectScopeSearchRequest(dn));
232     return Optional.ofNullable(searchResponse)
233         .map(SearchResponse::getEntry)
234         .map(entry -> entryMapper.mapAndComputeModifyRequest(domainObject, entry)
235             .map(modReq -> {
236               modify(modReq);
237               return entryMapper.map(entry);
238             })
239             .orElse(domainObject))  // domain object exists, but nothing to modify
240         .orElseGet(() -> {          // otherwise, save new ldap entry
241           String[] objectClasses = entryMapper.getObjectClasses();
242           if (objectClasses == null || objectClasses.length == 0) {
243             final ServiceException se = ServiceException.internalServerError(
244                 "Object classes must be specified to save a new ldap entry.",
245                 "org.bremersee:ldaptive-integration:d7aa5699-fd2e-45df-a863-97960e8095b8");
246             log.error("Saving domain object failed.", se);
247             throw se;
248           }
249           LdapEntry entry = new LdapEntry();
250           entryMapper.map(domainObject, entry);
251           entry.setDn(dn);
252           entry.addAttributes(new LdapAttribute("objectclass", objectClasses));
253           add(new AddRequest(dn, entry.getAttributes()));
254           return entryMapper.map(entry);
255         });
256   }
257 
258 }