org.pentaho.di.www.SocketRepository Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of kettle-engine Show documentation
Show all versions of kettle-engine Show documentation
Container pom for Pentaho Data Integration modules
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();
}
}
}