All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.pentaho.di.www.SocketRepository Maven / Gradle / Ivy

The newest version!
/*! ******************************************************************************
 *
 * Pentaho Data Integration
 *
 * Copyright (C) 2002-2017 by Hitachi Vantara : http://www.pentaho.com
 *
 *******************************************************************************
 *
 * 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.pentaho.di.www;

import java.io.IOException;
import java.net.BindException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.pentaho.di.core.logging.LogChannelInterface;

/**
 * This singleton keeps a repository of all the server sockets.
 *
 * @author matt
 *
 */
public class SocketRepository {

  /**
   * This map contains a link between a (clustered) transformation and their used server sockets
   */
  private Map socketMap;

  private LogChannelInterface log;

  public SocketRepository( LogChannelInterface log ) {
    this.log = log;
    socketMap = new HashMap();
  }

  private ServerSocket createServerSocket( int port ) throws IOException {
    ServerSocket serverSocket = new ServerSocket();
    serverSocket.setPerformancePreferences( 1, 2, 3 ); // order of importance: bandwidth, latency, connection time
    serverSocket.setReuseAddress( true );

    // It happens in high-paced environments where lots of sockets are opened and closed that the operating
    // system keeps a lock on a socket. Because of this we have to wait at least for one minute and on some platforms
    // up to 2 minutes.
    // Let's take 5 to make sure we can get a socket connection and we still get into trouble.
    //
    // mc: It sucks and blows at the same time that we have to do this but I couldn't find another solution.
    //
    try {
      serverSocket.bind( new InetSocketAddress( port ) );
    } catch ( BindException e ) {
      long totalWait = 0L;
      long startTime = System.currentTimeMillis();

      IOException ioException = null;
      log.logMinimal( "Carte socket repository : Starting a retry loop to bind the server socket on port "
        + port + ".  We retry for 5 minutes until the socket clears in your operating system." );
      while ( !serverSocket.isBound() && totalWait < 300000 ) {
        try {
          totalWait = System.currentTimeMillis() - startTime;
          log.logMinimal( "Carte socket repository : Retry binding the server socket on port "
            + port + " after a " + ( totalWait / 1000 ) + " seconds wait..." );
          Thread.sleep( 10000 ); // wait 10 seconds, try again...
          serverSocket.bind( new InetSocketAddress( port ), 100 );
        } catch ( IOException ioe ) {
          ioException = ioe;
        } catch ( Exception ex ) {
          serverSocket.close();
          throw new IOException( ex.getMessage() );
        }

        totalWait = System.currentTimeMillis() - startTime;
      }
      if ( !serverSocket.isBound() ) {
        serverSocket.close();
        throw ioException;
      }
      log.logDetailed( "Carte socket repository : Succesfully bound the server socket on port "
        + port + " after " + ( totalWait / 1000 ) + " seconds." );
    }
    return serverSocket;
  }

  public synchronized ServerSocket openServerSocket( int port, String user ) throws IOException {

    SocketRepositoryEntry entry = socketMap.get( port );
    if ( entry == null ) {

      ServerSocket serverSocket = createServerSocket( port );
      entry = new SocketRepositoryEntry( port, serverSocket, true, user );

      // Store the entry in the map too!
      //
      socketMap.put( port, entry );

    } else {
      // Verify that the socket is not in use...
      //
      if ( entry.isInUse() ) {
        throw new IOException( "Server socket on port " + port + " is already in use by [" + entry.getUser() + "]" );
      }
      if ( entry.getServerSocket().isClosed() ) {
        entry.setServerSocket( createServerSocket( port ) );
      }
      entry.setInUse( true );
    }

    return entry.getServerSocket();
  }

  /**
   * We don't actually ever close a server socket, we re-use them as much as possible.
   *
   * @param port
   * @throws IOException
   */
  public synchronized void releaseSocket( int port ) throws IOException {

    SocketRepositoryEntry entry = socketMap.get( port );
    if ( entry == null ) {
      throw new IOException( "Port to close was not found in the Carte socket repository!" );
    }
    entry.setInUse( false );
  }

  /**
   * @return the socketMap
   */
  public Map getSocketMap() {
    return socketMap;
  }

  /**
   * @param socketMap
   *          the socketMap to set
   */
  public void setSocketMap( Map socketMap ) {
    this.socketMap = socketMap;
  }

  /**
   * Closes all sockets on application end...
   *
   * @throws IOException
   *           in case there is an error
   */
  public synchronized void closeAll() {
    for ( Iterator> iterator = socketMap.entrySet().iterator();
          iterator.hasNext(); ) {
      Map.Entry repositoryEntry = iterator.next();
      SocketRepositoryEntry entry = repositoryEntry.getValue();
      ServerSocket serverSocket = entry.getServerSocket();
      try {
        if ( serverSocket != null ) {
          serverSocket.close();
          iterator.remove();
        }
      } catch ( IOException e ) {
        log.logError( "Carte socket repository : Failed to close socket during shutdown", e );
      }
    }
  }

  protected void finalize() throws Throwable {
    try {
      closeAll();
    } catch ( Exception e ) {
      // Ignore errors
    } finally {
      super.finalize();
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy