content: package org.apache.activemq.transport.auto.nio;
  
  import java.io.IOException;
  import java.net.Socket;
  import java.net.URI;
  import java.net.URISyntaxException;
  import java.nio.ByteBuffer;
  import java.util.HashMap;
  import java.util.Set;
  import java.util.concurrent.Future;
  
  import javax.net.ServerSocketFactory;
  import javax.net.ssl.SSLContext;
  import javax.net.ssl.SSLEngine;
  
  import org.apache.activemq.broker.BrokerService;
  import org.apache.activemq.broker.BrokerServiceAware;
  import org.apache.activemq.broker.TransportConnector;
  import org.apache.activemq.transport.Transport;
  import org.apache.activemq.transport.auto.AutoTcpTransportServer;
  import org.apache.activemq.transport.nio.AutoInitNioSSLTransport;
  import org.apache.activemq.transport.nio.NIOSSLTransport;
  import org.apache.activemq.transport.tcp.TcpTransport;
  import org.apache.activemq.transport.tcp.TcpTransport.InitBuffer;
  import org.apache.activemq.transport.tcp.TcpTransportFactory;
  import org.apache.activemq.transport.tcp.TcpTransportServer;
  import org.apache.activemq.util.IntrospectionSupport;
  import org.apache.activemq.wireformat.WireFormat;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  /**
   * Licensed to the Apache Software Foundation (ASF) under one or more
   * contributor license agreements.  See the NOTICE file distributed with
   * this work for additional information regarding copyright ownership.
   * The ASF licenses this file to You 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.
   */
  public class AutoNIOSSLTransportServer extends AutoTcpTransportServer {
  
      private static final Logger LOG = LoggerFactory.getLogger(AutoNIOSSLTransportServer.class);
      private SSLContext context;
  
      public AutoNIOSSLTransportServer(SSLContext context, TcpTransportFactory transportFactory, URI location, ServerSocketFactory serverSocketFactory,
              BrokerService brokerService, Set<String> enabledProtocols) throws IOException, URISyntaxException {
          super(transportFactory, location, serverSocketFactory, brokerService, enabledProtocols);
  
          this.context = context;
      }
  
      private boolean needClientAuth;
      private boolean wantClientAuth;
  
      protected Transport createTransport(Socket socket, WireFormat format, SSLEngine engine,
              InitBuffer initBuffer, ByteBuffer inputBuffer, TcpTransportFactory detectedFactory) throws IOException {
          NIOSSLTransport transport = new NIOSSLTransport(format, socket, engine, initBuffer, inputBuffer);
          if (context != null) {
              transport.setSslContext(context);
          }
  
          transport.setNeedClientAuth(needClientAuth);
          transport.setWantClientAuth(wantClientAuth);
  
  
          return transport;
      }
  
      @Override
      protected TcpTransport createTransport(Socket socket, WireFormat format) throws IOException {
          throw new UnsupportedOperationException("method not supported");
      }
  
      @Override
      public boolean isSslServer() {
          return true;
      }
  
      public boolean isNeedClientAuth() {
          return this.needClientAuth;
      }
  
      public void setNeedClientAuth(boolean value) {
          this.needClientAuth = value;
      }
  
      public boolean isWantClientAuth() {
          return this.wantClientAuth;
      }
  
      public void setWantClientAuth(boolean value) {
          this.wantClientAuth = value;
      }
  
  
      @Override
      protected TransportInfo configureTransport(final TcpTransportServer server, final Socket socket) throws Exception {
          //The SSLEngine needs to be initialized and handshake done to get the first command and detect the format
          //The wireformat doesn't need properties set here because we aren't using this format during the SSL handshake
          final AutoInitNioSSLTransport in = new AutoInitNioSSLTransport(wireFormatFactory.createWireFormat(), socket);
          if (context != null) {
              in.setSslContext(context);
          }
          //We need to set the transport options on the init transport so that the SSL options are set
          if (transportOptions != null) {
              //Clone the map because we will need to set the options later on the actual transport
              IntrospectionSupport.setProperties(in, new HashMap<>(transportOptions));
          }
  
          //Attempt to read enough bytes to detect the protocol until the timeout period
          //is reached
          Future<?> future = protocolDetectionExecutor.submit(new Runnable() {
              @Override
              public void run() {
                  try {
                      in.start();
                  } catch (Exception error) {
                      LOG.warn("Could not accept connection {}: {} ({})",
                              (in.getRemoteAddress() == null ? "" : "from " + in.getRemoteAddress()), error.getMessage(),
                              TransportConnector.getRootCause(error).getMessage());
                      throw new IllegalStateException("Could not complete Transport start", error);
                  }
  
                  int attempts = 0;
                  do {
                      if(attempts > 0) {
                          try {
                              //increase sleep period each attempt to prevent high cpu usage
                              //if the client is hung and not sending bytes
                              int sleep = attempts >= 1024 ? 1024 : 4 * attempts;
                              Thread.sleep(sleep);
                          } catch (InterruptedException e) {
                              break;
                          }
                      }
                      //In the future it might be better to register a nonblocking selector
                      //to be told when bytes are ready
                      in.serviceRead();
                      attempts++;
                  } while(in.getReceiveCounter() < 8 && !Thread.interrupted());
              }
          });
  
          try {
              //If this fails and throws an exception and the socket will be closed
              waitForProtocolDetectionFinish(future, in.getReceiveCounter());
          } finally {
              //call cancel in case task didn't complete which will interrupt the task
              future.cancel(true);
          }
          in.stop();
  
          InitBuffer initBuffer = new InitBuffer(in.getReceiveCounter(), ByteBuffer.allocate(in.getReadData().length));
          initBuffer.buffer.put(in.getReadData());
  
          ProtocolInfo protocolInfo = detectProtocol(in.getReadData());
  
          if (protocolInfo.detectedTransportFactory instanceof BrokerServiceAware) {
              ((BrokerServiceAware) protocolInfo.detectedTransportFactory).setBrokerService(brokerService);
          }
  
          WireFormat format = protocolInfo.detectedWireFormatFactory.createWireFormat();
          Transport transport = createTransport(socket, format, in.getSslSession(), initBuffer, in.getInputBuffer(), protocolInfo.detectedTransportFactory);
  
          return new TransportInfo(format, transport, protocolInfo.detectedTransportFactory);
      }
  
  }
  
  
  