LdaptiveTemplate.java
/*
* Copyright 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.bremersee.data.ldaptive;
import java.util.Optional;
import lombok.extern.slf4j.Slf4j;
import org.bremersee.exception.ServiceException;
import org.ldaptive.AddOperation;
import org.ldaptive.AddRequest;
import org.ldaptive.AttributeModification;
import org.ldaptive.BindOperation;
import org.ldaptive.BindRequest;
import org.ldaptive.BindResponse;
import org.ldaptive.CompareOperation;
import org.ldaptive.CompareRequest;
import org.ldaptive.CompareResponse;
import org.ldaptive.ConnectionFactory;
import org.ldaptive.DeleteOperation;
import org.ldaptive.DeleteRequest;
import org.ldaptive.LdapAttribute;
import org.ldaptive.LdapEntry;
import org.ldaptive.LdapException;
import org.ldaptive.ModifyDnOperation;
import org.ldaptive.ModifyDnRequest;
import org.ldaptive.ModifyOperation;
import org.ldaptive.ModifyRequest;
import org.ldaptive.Operation;
import org.ldaptive.Request;
import org.ldaptive.Result;
import org.ldaptive.ResultCode;
import org.ldaptive.SearchOperation;
import org.ldaptive.SearchRequest;
import org.ldaptive.SearchResponse;
import org.ldaptive.extended.ExtendedOperation;
import org.ldaptive.extended.ExtendedRequest;
import org.ldaptive.extended.ExtendedResponse;
import org.ldaptive.handler.ResultPredicate;
import org.springframework.util.Assert;
import org.springframework.util.ErrorHandler;
/**
* The template for executing ldap operations.
*
* @author Christian Bremer
*/
@Slf4j
@SuppressWarnings("WeakerAccess")
public class LdaptiveTemplate implements LdaptiveOperations, Cloneable {
private static final ResultPredicate NOT_COMPARE_RESULT = result -> !result.isSuccess()
&& result.getResultCode() != ResultCode.COMPARE_TRUE
&& result.getResultCode() != ResultCode.COMPARE_FALSE;
private static final ResultPredicate NOT_DELETE_RESULT = result -> !result.isSuccess()
&& result.getResultCode() != ResultCode.NO_SUCH_OBJECT;
private static final ResultPredicate NOT_FIND_RESULT = NOT_DELETE_RESULT;
private final ConnectionFactory connectionFactory;
private ErrorHandler errorHandler = new DefaultLdaptiveErrorHandler();
/**
* Instantiates a new ldap template.
*
* @param connectionFactory the connection factory
*/
public LdaptiveTemplate(ConnectionFactory connectionFactory) {
Assert.notNull(connectionFactory, "Connection factory must not be null.");
this.connectionFactory = connectionFactory;
}
@Override
public ConnectionFactory getConnectionFactory() {
return connectionFactory;
}
/**
* Sets error handler.
*
* @param errorHandler the error handler
*/
public void setErrorHandler(final ErrorHandler errorHandler) {
if (errorHandler != null) {
this.errorHandler = errorHandler;
}
}
/**
* Returns a new instance of this ldaptive template with the same connection factory and error handler.
*
* @return a new instance of this ldaptive template
*/
@SuppressWarnings("MethodDoesntCallSuperMethod")
@Override
public LdaptiveTemplate clone() {
return clone(null);
}
/**
* Returns a new instance of this ldaptive template with the same connection factory and the given error handler.
*
* @param errorHandler the new error handler
* @return the new instance of the ldaptive template
*/
public LdaptiveTemplate clone(final ErrorHandler errorHandler) {
final LdaptiveTemplate template = new LdaptiveTemplate(connectionFactory);
template.setErrorHandler(errorHandler);
return template;
}
private <Q extends Request, S extends Result> S execute(Operation<Q, S> operation, Q request) {
try {
return operation.execute(request);
} catch (Exception e) {
errorHandler.handleError(e);
return null;
}
}
@Override
public void add(AddRequest request) {
execute(
AddOperation.builder()
.factory(getConnectionFactory())
.throwIf(ResultPredicate.NOT_SUCCESS)
.build(),
request);
}
@Override
public boolean bind(BindRequest request) {
return Optional.ofNullable(execute(new BindOperation(getConnectionFactory()), request))
.map(BindResponse::isSuccess)
.orElse(false);
}
@Override
public boolean compare(CompareRequest request) {
return Optional.ofNullable(execute(
CompareOperation.builder()
.factory(getConnectionFactory())
.throwIf(NOT_COMPARE_RESULT)
.build(),
request))
.map(CompareResponse::isTrue)
.orElse(false);
}
@Override
public void delete(DeleteRequest request) {
execute(
DeleteOperation.builder()
.factory(getConnectionFactory())
.throwIf(NOT_DELETE_RESULT)
.build(),
request);
}
@Override
public ExtendedResponse executeExtension(ExtendedRequest request) {
return execute(
ExtendedOperation.builder()
.factory(getConnectionFactory())
.throwIf(ResultPredicate.NOT_SUCCESS)
.build(),
request);
}
@Override
public void modify(ModifyRequest request) {
if (request.getModifications() != null && request.getModifications().length > 0) {
execute(
ModifyOperation.builder()
.factory(getConnectionFactory())
.throwIf(ResultPredicate.NOT_SUCCESS)
.build(),
request);
}
}
@Override
public void modifyDn(ModifyDnRequest request) {
execute(
ModifyDnOperation.builder()
.factory(getConnectionFactory())
.throwIf(ResultPredicate.NOT_SUCCESS)
.build(),
request);
}
@Override
public SearchResponse search(SearchRequest request) {
return execute(
SearchOperation.builder()
.factory(getConnectionFactory())
.throwIf(NOT_FIND_RESULT)
.build(),
request);
}
@Override
public boolean exists(String dn) {
try {
SearchResponse response = SearchOperation.builder()
.factory(getConnectionFactory())
.throwIf(NOT_FIND_RESULT)
.build()
.execute(SearchRequest.objectScopeSearchRequest(dn));
return response.isSuccess();
} catch (LdapException e) {
errorHandler.handleError(e);
return false;
}
}
@Override
public <T> T save(T domainObject, LdaptiveEntryMapper<T> entryMapper) {
String dn = entryMapper.mapDn(domainObject);
SearchResponse searchResponse = execute(
SearchOperation.builder()
.factory(getConnectionFactory())
.throwIf(result -> result.getResultCode() != ResultCode.NO_SUCH_OBJECT
&& result.getResultCode() != ResultCode.SUCCESS)
.build(),
SearchRequest.objectScopeSearchRequest(dn));
return Optional.ofNullable(searchResponse)
.map(SearchResponse::getEntry)
.map(entry -> {
AttributeModification[] modifications = entryMapper.mapAndComputeModifications(domainObject, entry);
modify(new ModifyRequest(dn, modifications));
return entryMapper.map(entry);
})
.orElseGet(() -> {
String[] objectClasses = entryMapper.getObjectClasses();
if (objectClasses == null || objectClasses.length == 0) {
final ServiceException se = ServiceException.internalServerError(
"Object classes must be specified to save a new ldap entry.",
"org.bremersee:common-base-ldaptive:d7aa5699-fd2e-45df-a863-97960e8095b8");
log.error("Saving domain object failed.", se);
throw se;
}
LdapEntry entry = new LdapEntry();
entryMapper.map(domainObject, entry);
entry.setDn(dn);
entry.addAttributes(new LdapAttribute("objectclass", objectClasses));
add(new AddRequest(dn, entry.getAttributes()));
return entryMapper.map(entry);
});
}
}