package com.hwlcn.ldap.ldap.sdk; import java.io.IOException; import java.io.OutputStream; import java.net.Socket; import java.util.logging.Level; import java.util.concurrent.atomic.AtomicInteger; import javax.net.SocketFactory; import javax.net.ssl.SSLContext; import com.hwlcn.ldap.asn1.ASN1Buffer; import com.hwlcn.ldap.ldap.protocol.LDAPMessage; import com.hwlcn.ldap.util.DebugType; import com.hwlcn.core.annotation.InternalUseOnly; import static com.hwlcn.ldap.ldap.sdk.LDAPMessages.*; import static com.hwlcn.ldap.util.Debug.*; import static com.hwlcn.ldap.util.StaticUtils.*; @InternalUseOnly() final class LDAPConnectionInternals { private final AtomicInteger nextMessageID; private final boolean synchronousMode; private final int port; private final long connectTime; private final LDAPConnection connection; private final LDAPConnectionReader connectionReader; private volatile OutputStream outputStream; private final Socket socket; private final String host; private static final ThreadLocal<ASN1Buffer> asn1Buffers = new ThreadLocal<ASN1Buffer>(); LDAPConnectionInternals(final LDAPConnection connection, final LDAPConnectionOptions options, final SocketFactory socketFactory, final String host, final int port, final int timeout) throws IOException { this.connection = connection; this.host = host; this.port = port; if (options.captureConnectStackTrace()) { connection.setConnectStackTrace(Thread.currentThread().getStackTrace()); } connectTime = System.currentTimeMillis(); nextMessageID = new AtomicInteger(0); synchronousMode = options.useSynchronousMode(); try { final ConnectThread connectThread = new ConnectThread(socketFactory, host, port); connectThread.start(); socket = connectThread.getConnectedSocket(timeout); } catch (LDAPException le) { debugException(le); throw new IOException(le.getMessage()); } if (options.getReceiveBufferSize() > 0) { socket.setReceiveBufferSize(options.getReceiveBufferSize()); } if (options.getSendBufferSize() > 0) { socket.setSendBufferSize(options.getSendBufferSize()); } try { debugConnect(host, port, connection); socket.setKeepAlive(options.useKeepAlive()); socket.setReuseAddress(options.useReuseAddress()); socket.setSoLinger(options.useLinger(), options.getLingerTimeoutSeconds()); socket.setTcpNoDelay(options.useTCPNoDelay()); outputStream = socket.getOutputStream(); connectionReader = new LDAPConnectionReader(connection, this); } catch (IOException ioe) { debugException(ioe); try { socket.close(); } catch (Exception e) { debugException(e); } throw ioe; } } void startConnectionReader() { if (! synchronousMode) { connectionReader.start(); } } LDAPConnection getConnection() { return connection; } LDAPConnectionReader getConnectionReader() { return connectionReader; } String getHost() { return host; } int getPort() { return port; } Socket getSocket() { return socket; } OutputStream getOutputStream() { return outputStream; } boolean isConnected() { return socket.isConnected(); } boolean synchronousMode() { return synchronousMode; } void convertToTLS(final SSLContext sslContext) throws LDAPException { outputStream = connectionReader.doStartTLS(sslContext); } int nextMessageID() { int msgID = nextMessageID.incrementAndGet(); if (msgID > 0) { return msgID; } while (true) { if (nextMessageID.compareAndSet(msgID, 1)) { return 1; } msgID = nextMessageID.incrementAndGet(); if (msgID > 0) { return msgID; } } } void registerResponseAcceptor(final int messageID, final ResponseAcceptor responseAcceptor) throws LDAPException { if (! isConnected()) { final LDAPConnectionOptions connectionOptions = connection.getConnectionOptions(); final boolean closeRequested = connection.closeRequested(); if (connectionOptions.autoReconnect() && (! closeRequested)) { connection.reconnect(); connection.registerResponseAcceptor(messageID, responseAcceptor); } else { throw new LDAPException(ResultCode.SERVER_DOWN, ERR_CONN_NOT_ESTABLISHED.get()); } } connectionReader.registerResponseAcceptor(messageID, responseAcceptor); } void deregisterResponseAcceptor(final int messageID) { connectionReader.deregisterResponseAcceptor(messageID); } void sendMessage(final LDAPMessage message, final boolean allowRetry) throws LDAPException { if (! isConnected()) { throw new LDAPException(ResultCode.SERVER_DOWN, ERR_CONN_NOT_ESTABLISHED.get()); } ASN1Buffer buffer = asn1Buffers.get(); if (buffer == null) { buffer = new ASN1Buffer(); asn1Buffers.set(buffer); } buffer.clear(); try { message.writeTo(buffer); } catch (final LDAPRuntimeException lre) { debugException(lre); lre.throwLDAPException(); } try { final OutputStream os = outputStream; buffer.writeTo(os); os.flush(); } catch (IOException ioe) { debugException(ioe); if (message.getProtocolOpType() == LDAPMessage.PROTOCOL_OP_TYPE_UNBIND_REQUEST) { return; } final LDAPConnectionOptions connectionOptions = connection.getConnectionOptions(); final boolean closeRequested = connection.closeRequested(); if (allowRetry && (! closeRequested) && (! connection.synchronousMode())) { connection.reconnect(); try { sendMessage(message, false); return; } catch (final Exception e) { debugException(e); } } throw new LDAPException(ResultCode.SERVER_DOWN, ERR_CONN_SEND_ERROR.get(host + ':' + port, getExceptionMessage(ioe)), ioe); } catch (Exception e) { debugException(e); throw new LDAPException(ResultCode.LOCAL_ERROR, ERR_CONN_ENCODE_ERROR.get(host + ':' + port, getExceptionMessage(e)), e); } finally { if (buffer.zeroBufferOnClear()) { buffer.clear(); } } } void close() { DisconnectInfo disconnectInfo = connection.getDisconnectInfo(); if (disconnectInfo == null) { disconnectInfo = connection.setDisconnectInfo( new DisconnectInfo(connection, DisconnectType.UNKNOWN, null, null)); } final boolean closedByFinalizer = ((disconnectInfo.getType() == DisconnectType.CLOSED_BY_FINALIZER) && socket.isConnected()); try { connectionReader.close(false); } catch (Exception e) { debugException(e); } try { outputStream.close(); } catch (Exception e) { debugException(e); } try { socket.close(); } catch (Exception e) { debugException(e); } debugDisconnect(host, port, connection, disconnectInfo.getType(), disconnectInfo.getMessage(), disconnectInfo.getCause()); if (closedByFinalizer && debugEnabled(DebugType.LDAP)) { debug(Level.WARNING, DebugType.LDAP, "Connection closed by LDAP SDK finalizer: " + toString()); } disconnectInfo.notifyDisconnectHandler(); } public long getConnectTime() { if (isConnected()) { return connectTime; } else { return -1L; } } @Override() public String toString() { final StringBuilder buffer = new StringBuilder(); toString(buffer); return buffer.toString(); } public void toString(final StringBuilder buffer) { buffer.append("LDAPConnectionInternals(host='"); buffer.append(host); buffer.append("', port="); buffer.append(port); buffer.append(", connected="); buffer.append(socket.isConnected()); buffer.append(", nextMessageID="); buffer.append(nextMessageID.get()); buffer.append(')'); } }