package com.hwlcn.ldap.ldap.sdk;
import java.util.Arrays;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import com.hwlcn.ldap.asn1.ASN1Buffer;
import com.hwlcn.ldap.asn1.ASN1BufferSequence;
import com.hwlcn.ldap.asn1.ASN1Element;
import com.hwlcn.ldap.asn1.ASN1Integer;
import com.hwlcn.ldap.asn1.ASN1OctetString;
import com.hwlcn.ldap.asn1.ASN1Sequence;
import com.hwlcn.ldap.ldap.protocol.LDAPMessage;
import com.hwlcn.ldap.ldap.protocol.LDAPResponse;
import com.hwlcn.ldap.ldap.protocol.ProtocolOp;
import com.hwlcn.core.annotation.InternalUseOnly;
import com.hwlcn.ldap.util.LDAPSDKUsageException;
import com.hwlcn.core.annotation.NotMutable;
import com.hwlcn.core.annotation.ThreadSafety;
import com.hwlcn.ldap.util.ThreadSafetyLevel;
import static com.hwlcn.ldap.ldap.sdk.LDAPMessages.*;
import static com.hwlcn.ldap.util.Debug.*;
import static com.hwlcn.ldap.util.StaticUtils.*;
@NotMutable()
@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
public final class SimpleBindRequest
extends BindRequest
implements ResponseAcceptor, ProtocolOp
{
private static final byte CRED_TYPE_SIMPLE = (byte) 0x80;
private static final ASN1OctetString NO_BIND_DN = new ASN1OctetString();
private static final ASN1OctetString NO_PASSWORD =
new ASN1OctetString(CRED_TYPE_SIMPLE);
private static final long serialVersionUID = 4725871243149974407L;
private int messageID = -1;
private final ASN1OctetString bindDN;
private final ASN1OctetString password;
private final LinkedBlockingQueue<LDAPResponse> responseQueue =
new LinkedBlockingQueue<LDAPResponse>();
private final PasswordProvider passwordProvider;
public SimpleBindRequest()
{
this(NO_BIND_DN, NO_PASSWORD, null, NO_CONTROLS);
}
public SimpleBindRequest(final String bindDN, final String password)
{
this(bindDN, password, NO_CONTROLS);
}
public SimpleBindRequest(final String bindDN, final byte[] password)
{
this(bindDN, password, NO_CONTROLS);
}
public SimpleBindRequest(final DN bindDN, final String password)
{
this(bindDN, password, NO_CONTROLS);
}
public SimpleBindRequest(final DN bindDN, final byte[] password)
{
this(bindDN, password, NO_CONTROLS);
}
public SimpleBindRequest(final String bindDN, final String password,
final Control... controls)
{
super(controls);
if (bindDN == null)
{
this.bindDN = NO_BIND_DN;
}
else
{
this.bindDN = new ASN1OctetString(bindDN);
}
if (password == null)
{
this.password = NO_PASSWORD;
}
else
{
this.password = new ASN1OctetString(CRED_TYPE_SIMPLE, password);
}
passwordProvider = null;
}
public SimpleBindRequest(final String bindDN, final byte[] password,
final Control... controls)
{
super(controls);
if (bindDN == null)
{
this.bindDN = NO_BIND_DN;
}
else
{
this.bindDN = new ASN1OctetString(bindDN);
}
if (password == null)
{
this.password = NO_PASSWORD;
}
else
{
this.password = new ASN1OctetString(CRED_TYPE_SIMPLE, password);
}
passwordProvider = null;
}
public SimpleBindRequest(final DN bindDN, final String password,
final Control... controls)
{
super(controls);
if (bindDN == null)
{
this.bindDN = NO_BIND_DN;
}
else
{
this.bindDN = new ASN1OctetString(bindDN.toString());
}
if (password == null)
{
this.password = NO_PASSWORD;
}
else
{
this.password = new ASN1OctetString(CRED_TYPE_SIMPLE, password);
}
passwordProvider = null;
}
public SimpleBindRequest(final DN bindDN, final byte[] password,
final Control... controls)
{
super(controls);
if (bindDN == null)
{
this.bindDN = NO_BIND_DN;
}
else
{
this.bindDN = new ASN1OctetString(bindDN.toString());
}
if (password == null)
{
this.password = NO_PASSWORD;
}
else
{
this.password = new ASN1OctetString(CRED_TYPE_SIMPLE, password);
}
passwordProvider = null;
}
public SimpleBindRequest(final String bindDN,
final PasswordProvider passwordProvider,
final Control... controls)
{
super(controls);
this.bindDN = new ASN1OctetString(bindDN);
this.passwordProvider = passwordProvider;
password = null;
}
public SimpleBindRequest(final DN bindDN,
final PasswordProvider passwordProvider,
final Control... controls)
{
super(controls);
this.bindDN = new ASN1OctetString(bindDN.toString());
this.passwordProvider = passwordProvider;
password = null;
}
private SimpleBindRequest(final ASN1OctetString bindDN,
final ASN1OctetString password,
final PasswordProvider passwordProvider,
final Control... controls)
{
super(controls);
this.bindDN = bindDN;
this.password = password;
this.passwordProvider = passwordProvider;
}
public String getBindDN()
{
return bindDN.stringValue();
}
public ASN1OctetString getPassword()
{
return password;
}
public PasswordProvider getPasswordProvider()
{
return passwordProvider;
}
public byte getProtocolOpType()
{
return LDAPMessage.PROTOCOL_OP_TYPE_BIND_REQUEST;
}
public void writeTo(final ASN1Buffer buffer)
{
final ASN1BufferSequence requestSequence =
buffer.beginSequence(LDAPMessage.PROTOCOL_OP_TYPE_BIND_REQUEST);
buffer.addElement(VERSION_ELEMENT);
buffer.addElement(bindDN);
if (passwordProvider == null)
{
buffer.addElement(password);
}
else
{
byte[] pwBytes;
try
{
pwBytes = passwordProvider.getPasswordBytes();
}
catch (final LDAPException le)
{
debugException(le);
throw new LDAPRuntimeException(le);
}
final ASN1OctetString pw = new ASN1OctetString(CRED_TYPE_SIMPLE, pwBytes);
buffer.addElement(pw);
buffer.setZeroBufferOnClear();
Arrays.fill(pwBytes, (byte) 0x00);
}
requestSequence.end();
}
public ASN1Element encodeProtocolOp()
throws LDAPSDKUsageException
{
if (password == null)
{
throw new LDAPSDKUsageException(
ERR_SIMPLE_BIND_ENCODE_PROTOCOL_OP_WITH_PROVIDER.get());
}
return new ASN1Sequence(LDAPMessage.PROTOCOL_OP_TYPE_BIND_REQUEST,
new ASN1Integer(3),
bindDN,
password);
}
@Override()
protected BindResult process(final LDAPConnection connection, final int depth)
throws LDAPException
{
if (connection.synchronousMode())
{
return processSync(connection,
connection.getConnectionOptions().autoReconnect());
}
if (password != null)
{
if ((bindDN.getValue().length > 0) && (password.getValue().length == 0) &&
connection.getConnectionOptions().bindWithDNRequiresPassword())
{
final LDAPException le = new LDAPException(ResultCode.PARAM_ERROR,
ERR_SIMPLE_BIND_DN_WITHOUT_PASSWORD.get());
debugCodingError(le);
throw le;
}
}
messageID = connection.nextMessageID();
final LDAPMessage message = new LDAPMessage(messageID, this, getControls());
connection.registerResponseAcceptor(messageID, this);
try
{
debugLDAPRequest(this);
final long requestTime = System.nanoTime();
connection.getConnectionStatistics().incrementNumBindRequests();
connection.sendMessage(message);
final LDAPResponse response;
try
{
final long responseTimeout = getResponseTimeoutMillis(connection);
if (responseTimeout > 0)
{
response = responseQueue.poll(responseTimeout, TimeUnit.MILLISECONDS);
}
else
{
response = responseQueue.take();
}
}
catch (InterruptedException ie)
{
debugException(ie);
throw new LDAPException(ResultCode.LOCAL_ERROR,
ERR_BIND_INTERRUPTED.get(connection.getHostPort()), ie);
}
return handleResponse(connection, response, requestTime, false);
}
finally
{
connection.deregisterResponseAcceptor(messageID);
}
}
private BindResult processSync(final LDAPConnection connection,
final boolean allowRetry)
throws LDAPException
{
// Create the LDAP message.
messageID = connection.nextMessageID();
final LDAPMessage message =
new LDAPMessage(messageID, this, getControls());
try
{
connection.getConnectionInternals(true).getSocket().setSoTimeout(
(int) getResponseTimeoutMillis(connection));
}
catch (Exception e)
{
debugException(e);
}
final long requestTime = System.nanoTime();
debugLDAPRequest(this);
connection.getConnectionStatistics().incrementNumBindRequests();
try
{
connection.sendMessage(message);
}
catch (final LDAPException le)
{
debugException(le);
if (allowRetry)
{
final BindResult bindResult = reconnectAndRetry(connection,
le.getResultCode());
if (bindResult != null)
{
return bindResult;
}
}
}
while (true)
{
final LDAPResponse response = connection.readResponse(messageID);
if (response instanceof IntermediateResponse)
{
final IntermediateResponseListener listener =
getIntermediateResponseListener();
if (listener != null)
{
listener.intermediateResponseReturned(
(IntermediateResponse) response);
}
}
else
{
return handleResponse(connection, response, requestTime, allowRetry);
}
}
}
private BindResult handleResponse(final LDAPConnection connection,
final LDAPResponse response,
final long requestTime,
final boolean allowRetry)
throws LDAPException
{
if (response == null)
{
final long waitTime = nanosToMillis(System.nanoTime() - requestTime);
throw new LDAPException(ResultCode.TIMEOUT,
ERR_BIND_CLIENT_TIMEOUT.get(waitTime, connection.getHostPort()));
}
connection.getConnectionStatistics().incrementNumBindResponses(
System.nanoTime() - requestTime);
if (response instanceof ConnectionClosedResponse)
{
if (allowRetry)
{
final BindResult retryResult = reconnectAndRetry(connection,
ResultCode.SERVER_DOWN);
if (retryResult != null)
{
return retryResult;
}
}
final ConnectionClosedResponse ccr = (ConnectionClosedResponse) response;
final String message = ccr.getMessage();
if (message == null)
{
throw new LDAPException(ccr.getResultCode(),
ERR_CONN_CLOSED_WAITING_FOR_BIND_RESPONSE.get(
connection.getHostPort(), toString()));
}
else
{
throw new LDAPException(ccr.getResultCode(),
ERR_CONN_CLOSED_WAITING_FOR_BIND_RESPONSE_WITH_MESSAGE.get(
connection.getHostPort(), toString(), message));
}
}
final BindResult bindResult = (BindResult) response;
if (allowRetry)
{
final BindResult retryResult = reconnectAndRetry(connection,
bindResult.getResultCode());
if (retryResult != null)
{
return retryResult;
}
}
return bindResult;
}
private BindResult reconnectAndRetry(final LDAPConnection connection,
final ResultCode resultCode)
{
try
{
switch (resultCode.intValue())
{
case ResultCode.SERVER_DOWN_INT_VALUE:
case ResultCode.DECODING_ERROR_INT_VALUE:
case ResultCode.CONNECT_ERROR_INT_VALUE:
connection.reconnect();
return processSync(connection, false);
}
}
catch (final Exception e)
{
debugException(e);
}
return null;
}
@Override()
public SimpleBindRequest getRebindRequest(final String host, final int port)
{
return new SimpleBindRequest(bindDN, password, passwordProvider,
getControls());
}
@InternalUseOnly()
public void responseReceived(final LDAPResponse response)
throws LDAPException
{
try
{
responseQueue.put(response);
}
catch (Exception e)
{
debugException(e);
throw new LDAPException(ResultCode.LOCAL_ERROR,
ERR_EXCEPTION_HANDLING_RESPONSE.get(getExceptionMessage(e)), e);
}
}
@Override()
public String getBindType()
{
return "SIMPLE";
}
@Override()
public int getLastMessageID()
{
return messageID;
}
@Override()
public SimpleBindRequest duplicate()
{
return duplicate(getControls());
}
@Override()
public SimpleBindRequest duplicate(final Control[] controls)
{
final SimpleBindRequest bindRequest =
new SimpleBindRequest(bindDN, password, passwordProvider, controls);
bindRequest.setResponseTimeoutMillis(getResponseTimeoutMillis(null));
return bindRequest;
}
@Override()
public void toString(final StringBuilder buffer)
{
buffer.append("SimpleBindRequest(dn='");
buffer.append(bindDN);
buffer.append('\'');
final Control[] controls = getControls();
if (controls.length > 0)
{
buffer.append(", controls={");
for (int i=0; i < controls.length; i++)
{
if (i > 0)
{
buffer.append(", ");
}
buffer.append(controls[i]);
}
buffer.append('}');
}
buffer.append(')');
}
}