/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, * add the following below this CDDL HEADER, with the fields enclosed * by brackets "[]" replaced with your own identifying information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2009 Sun Microsystems, Inc. */ package com.sun.opends.sdk.ldap; import static com.sun.opends.sdk.ldap.LDAPConstants.*; import org.opends.sdk.ldap.LDAPEncoder; import org.opends.sdk.ldap.ResolvedSchema; import java.io.EOFException; import java.io.IOException; import java.net.InetSocketAddress; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicInteger; import javax.net.ssl.SSLContext; import javax.security.sasl.SaslException; import org.opends.sdk.*; import org.opends.sdk.controls.Control; import org.opends.sdk.controls.ControlDecoder; import org.opends.sdk.extensions.StartTLSRequest; import org.opends.sdk.requests.*; import org.opends.sdk.responses.*; import org.opends.sdk.sasl.SASLBindRequest; import org.opends.sdk.sasl.SASLContext; import org.opends.sdk.schema.Schema; import com.sun.grizzly.filterchain.Filter; import com.sun.grizzly.filterchain.FilterChain; import com.sun.grizzly.filterchain.StreamTransformerFilter; import com.sun.grizzly.ssl.*; import com.sun.grizzly.streams.StreamWriter; import com.sun.opends.sdk.util.Validator; /** * LDAP connection implementation. *
* TODO: handle illegal state exceptions.
*/
public final class LDAPConnection extends
AbstractAsynchronousConnection implements AsynchronousConnection
{
private final class LDAPMessageHandlerImpl extends
AbstractLDAPMessageHandler
{
/**
* {@inheritDoc}
*/
@Override
public void handleAddResult(int messageID, Result result)
{
AbstractLDAPFutureResultImpl> pendingRequest = pendingRequests
.remove(messageID);
if (pendingRequest != null)
{
if (pendingRequest instanceof LDAPFutureResultImpl)
{
LDAPFutureResultImpl future = (LDAPFutureResultImpl) pendingRequest;
if (future.getRequest() instanceof AddRequest)
{
future.setResultOrError(result);
return;
}
}
handleIncorrectResponse(pendingRequest);
}
}
/**
* {@inheritDoc}
*/
@Override
public void handleBindResult(int messageID, BindResult result)
{
AbstractLDAPFutureResultImpl> pendingRequest = pendingRequests
.remove(messageID);
if (pendingRequest != null)
{
if (pendingRequest instanceof LDAPBindFutureResultImpl)
{
LDAPBindFutureResultImpl future = ((LDAPBindFutureResultImpl) pendingRequest);
BindRequest request = future.getRequest();
if (request instanceof SASLBindRequest>)
{
SASLBindRequest> saslBind = (SASLBindRequest>) request;
SASLContext saslContext = future.getSASLContext();
if ((result.getResultCode() == ResultCode.SUCCESS || result
.getResultCode() == ResultCode.SASL_BIND_IN_PROGRESS)
&& !saslContext.isComplete())
{
try
{
saslContext.evaluateCredentials(result
.getServerSASLCredentials());
}
catch (SaslException e)
{
pendingBindOrStartTLS = -1;
// FIXME: I18N need to have a better error message.
// FIXME: Is this the best result code?
Result errorResult = Responses.newResult(
ResultCode.CLIENT_SIDE_LOCAL_ERROR)
.setDiagnosticMessage(
"An error occurred during SASL authentication")
.setCause(e);
future.adaptErrorResult(errorResult);
return;
}
}
if (result.getResultCode() == ResultCode.SASL_BIND_IN_PROGRESS)
{
// The server is expecting a multi stage bind response.
messageID = nextMsgID.getAndIncrement();
ASN1StreamWriter asn1Writer = connFactory
.getASN1Writer(streamWriter);
try
{
synchronized (writeLock)
{
pendingRequests.put(messageID, future);
try
{
LDAPEncoder.encodeBindRequest(asn1Writer,
messageID, 3, saslBind, saslContext
.getSASLCredentials());
asn1Writer.flush();
}
catch (IOException e)
{
pendingRequests.remove(messageID);
// FIXME: what other sort of IOExceptions can be
// thrown?
// FIXME: Is this the best result code?
Result errorResult = Responses.newResult(
ResultCode.CLIENT_SIDE_ENCODING_ERROR)
.setCause(e);
connectionErrorOccurred(errorResult);
future.adaptErrorResult(errorResult);
}
}
}
finally
{
connFactory.releaseASN1Writer(asn1Writer);
}
return;
}
if ((result.getResultCode() == ResultCode.SUCCESS)
&& saslContext.isSecure())
{
// The connection needs to be secured by the SASL
// mechanism.
installFilter(SASLFilter.getInstance(saslContext,
connection));
}
}
pendingBindOrStartTLS = -1;
future.setResultOrError(result);
}
else
{
handleIncorrectResponse(pendingRequest);
}
}
}
/**
* {@inheritDoc}
*/
@Override
public void handleCompareResult(int messageID, CompareResult result)
{
AbstractLDAPFutureResultImpl> pendingRequest = pendingRequests
.remove(messageID);
if (pendingRequest != null)
{
if (pendingRequest instanceof LDAPCompareFutureResultImpl)
{
LDAPCompareFutureResultImpl future = (LDAPCompareFutureResultImpl) pendingRequest;
future.setResultOrError(result);
}
else
{
handleIncorrectResponse(pendingRequest);
}
}
}
/**
* {@inheritDoc}
*/
@Override
public void handleDeleteResult(int messageID, Result result)
{
AbstractLDAPFutureResultImpl> pendingRequest = pendingRequests
.remove(messageID);
if (pendingRequest != null)
{
if (pendingRequest instanceof LDAPFutureResultImpl)
{
LDAPFutureResultImpl future = (LDAPFutureResultImpl) pendingRequest;
if (future.getRequest() instanceof DeleteRequest)
{
future.setResultOrError(result);
return;
}
}
handleIncorrectResponse(pendingRequest);
}
}
/**
* {@inheritDoc}
*/
public void handleException(Throwable throwable)
{
Result errorResult;
if (throwable instanceof EOFException)
{
// FIXME: Is this the best result code?
errorResult = Responses.newResult(
ResultCode.CLIENT_SIDE_SERVER_DOWN).setCause(throwable);
}
else
{
// FIXME: what other sort of IOExceptions can be thrown?
// FIXME: Is this the best result code?
errorResult = Responses.newResult(
ResultCode.CLIENT_SIDE_DECODING_ERROR).setCause(throwable);
}
connectionErrorOccurred(errorResult);
}
/**
* {@inheritDoc}
*/
@Override
public void handleExtendedResult(int messageID,
GenericExtendedResult result)
{
if (messageID == 0)
{
if ((result.getResponseName() != null)
&& result.getResponseName().equals(
OID_NOTICE_OF_DISCONNECTION))
{
Result errorResult = Responses.newResult(
result.getResultCode()).setDiagnosticMessage(
result.getDiagnosticMessage());
close(null, true, errorResult);
return;
}
else
{
// Unsolicited notification received.
synchronized (writeLock)
{
if (isClosed)
{
// Don't notify after connection is closed.
return;
}
for (ConnectionEventListener listener : listeners)
{
listener
.connectionReceivedUnsolicitedNotification(result);
}
}
}
}
AbstractLDAPFutureResultImpl> pendingRequest = pendingRequests
.remove(messageID);
if (pendingRequest instanceof LDAPExtendedFutureResultImpl>)
{
LDAPExtendedFutureResultImpl> extendedFuture = ((LDAPExtendedFutureResultImpl>) pendingRequest);
try
{
handleExtendedResult0(extendedFuture, result);
}
catch (DecodeException de)
{
// FIXME: should the connection be closed as well?
Result errorResult = Responses.newResult(
ResultCode.CLIENT_SIDE_DECODING_ERROR)
.setDiagnosticMessage(de.getLocalizedMessage()).setCause(
de);
extendedFuture.adaptErrorResult(errorResult);
}
}
else
{
handleIncorrectResponse(pendingRequest);
}
}
/**
* {@inheritDoc}
*/
@Override
public void handleIntermediateResponse(int messageID,
GenericIntermediateResponse response)
{
AbstractLDAPFutureResultImpl> pendingRequest = pendingRequests
.remove(messageID);
if (pendingRequest != null)
{
handleIncorrectResponse(pendingRequest);
// FIXME: intermediate responses can occur for all operations.
// if (pendingRequest instanceof LDAPExtendedFutureResultImpl)
// {
// LDAPExtendedFutureResultImpl extendedFuture =
// ((LDAPExtendedFutureResultImpl) pendingRequest);
// ExtendedRequest request = extendedFuture.getRequest();
//
// try
// {
// IntermediateResponse decodedResponse =
// request.getExtendedOperation()
// .decodeIntermediateResponse(
// response.getResponseName(),
// response.getResponseValue());
// extendedFuture.handleIntermediateResponse(decodedResponse);
// }
// catch (DecodeException de)
// {
// pendingRequest.failure(de);
// }
// }
// else
// {
// handleIncorrectResponse(pendingRequest);
// }
}
}
/**
* {@inheritDoc}
*/
@Override
public void handleModifyDNResult(int messageID, Result result)
{
AbstractLDAPFutureResultImpl> pendingRequest = pendingRequests
.remove(messageID);
if (pendingRequest != null)
{
if (pendingRequest instanceof LDAPFutureResultImpl)
{
LDAPFutureResultImpl future = (LDAPFutureResultImpl) pendingRequest;
if (future.getRequest() instanceof ModifyDNRequest)
{
future.setResultOrError(result);
return;
}
}
handleIncorrectResponse(pendingRequest);
}
}
/**
* {@inheritDoc}
*/
@Override
public void handleModifyResult(int messageID, Result result)
{
AbstractLDAPFutureResultImpl> pendingRequest = pendingRequests
.remove(messageID);
if (pendingRequest != null)
{
if (pendingRequest instanceof LDAPFutureResultImpl)
{
LDAPFutureResultImpl future = (LDAPFutureResultImpl) pendingRequest;
if (future.getRequest() instanceof ModifyRequest)
{
future.setResultOrError(result);
return;
}
}
handleIncorrectResponse(pendingRequest);
}
}
/**
* {@inheritDoc}
*/
@Override
public void handleSearchResult(int messageID, Result result)
{
AbstractLDAPFutureResultImpl> pendingRequest = pendingRequests
.remove(messageID);
if (pendingRequest != null)
{
if (pendingRequest instanceof LDAPSearchFutureResultImpl)
{
((LDAPSearchFutureResultImpl) pendingRequest)
.setResultOrError(result);
}
else
{
handleIncorrectResponse(pendingRequest);
}
}
}
/**
* {@inheritDoc}
*/
@Override
public void handleSearchResultEntry(int messageID,
SearchResultEntry entry)
{
AbstractLDAPFutureResultImpl> pendingRequest = pendingRequests
.get(messageID);
if (pendingRequest != null)
{
if (pendingRequest instanceof LDAPSearchFutureResultImpl)
{
((LDAPSearchFutureResultImpl) pendingRequest)
.handleSearchResultEntry(entry);
}
else
{
handleIncorrectResponse(pendingRequest);
}
}
}
/**
* {@inheritDoc}
*/
@Override
public void handleSearchResultReference(int messageID,
SearchResultReference reference)
{
AbstractLDAPFutureResultImpl> pendingRequest = pendingRequests
.get(messageID);
if (pendingRequest != null)
{
if (pendingRequest instanceof LDAPSearchFutureResultImpl)
{
((LDAPSearchFutureResultImpl) pendingRequest)
.handleSearchResultReference(reference);
}
else
{
handleIncorrectResponse(pendingRequest);
}
}
}
@Override
public Control decodeResponseControl(int messageID, String oid,
boolean isCritical, ByteString value, Schema schema)
throws DecodeException
{
ControlDecoder> decoder = connFactory.getControlDecoder(oid);
if (decoder != null)
{
return decoder.decode(isCritical, value, schema);
}
return super.decodeResponseControl(messageID, oid, isCritical,
value, schema);
}
public ResolvedSchema resolveSchema(String dn)
throws DecodeException
{
DN initialDN;
try
{
initialDN = DN.valueOf(dn, schema);
}
catch (LocalizedIllegalArgumentException e)
{
throw DecodeException.error(e.getMessageObject());
}
return new ResolvedSchemaImpl(schema, initialDN);
}
public Schema getDefaultSchema()
{
return schema;
}
}
private static final class ResolvedSchemaImpl implements
ResolvedSchema
{
private final DN initialDN;
private final Schema schema;
private ResolvedSchemaImpl(Schema schema, DN initialDN)
{
this.schema = schema;
this.initialDN = initialDN;
}
public AttributeDescription decodeAttributeDescription(
String attributeDescription) throws DecodeException
{
try
{
return AttributeDescription.valueOf(attributeDescription,
schema);
}
catch (LocalizedIllegalArgumentException e)
{
throw DecodeException.error(e.getMessageObject());
}
}
public DN decodeDN(String dn) throws DecodeException
{
try
{
return DN.valueOf(dn, schema);
}
catch (LocalizedIllegalArgumentException e)
{
throw DecodeException.error(e.getMessageObject());
}
}
public RDN decodeRDN(String rdn) throws DecodeException
{
try
{
return RDN.valueOf(rdn, schema);
}
catch (LocalizedIllegalArgumentException e)
{
throw DecodeException.error(e.getMessageObject());
}
}
public DN getInitialDN()
{
return initialDN;
}
public Schema getSchema()
{
return schema;
}
}
private final Schema schema;
private final com.sun.grizzly.Connection> connection;
private Result connectionInvalidReason;
private final LDAPConnectionFactoryImpl connFactory;
private FilterChain customFilterChain;
private final LDAPMessageHandler handler = new LDAPMessageHandlerImpl();
private boolean isClosed = false;
private final List