/*
* JacORB - a free Java ORB
*
* Copyright (C) 1997-2008 Gerald Brose.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package org.jacorb.orb.iiop;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.net.SocketTimeoutException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.jacorb.config.*;
import org.jacorb.orb.factory.SocketFactory;
import org.jacorb.orb.giop.TransportManager;
import org.jacorb.orb.listener.TCPConnectionEvent;
import org.jacorb.orb.listener.TCPConnectionListener;
import org.omg.CORBA.TIMEOUT;
/**
* @author Nicolas Noffke
* @author Andre Spiegel
* @version $Id$
*/
public class ClientIIOPConnection
extends IIOPConnection
implements Configurable
{
private int timeout = 0;
private int ssl_port = -1;
private int noOfRetries = 5;
private int retryInterval = 0;
private boolean doSupportSSL = false;
private TransportManager transportManager;
private TCPConnectionListener connectionListener;
private boolean keepAlive;
//for testing purposes only: # of open transports
//used by org.jacorb.test.orb.connection[Client|Server]ConnectionTimeoutTest
public static int openTransports = 0;
public ClientIIOPConnection()
{
super();
use_ssl = false;
}
public void configure(Configuration configuration)
throws ConfigurationException
{
super.configure(configuration);
//get the client-side timeout property value
timeout =
configuration.getAttributeAsInteger("jacorb.connection.client.idle_timeout",0 );
noOfRetries =
configuration.getAttributeAsInteger("jacorb.retries", 5);
retryInterval =
configuration.getAttributeAsInteger("jacorb.retry_interval",500);
doSupportSSL =
configuration.getAttributeAsBoolean("jacorb.security.support_ssl", false);
transportManager =
this.configuration.getORB().getTransportManager();
keepAlive = configuration.getAttributeAsBoolean("jacorb.connection.client.keepalive", false);
connectionListener = transportManager.getSocketFactoryManager().getTCPListener();
}
/**
* Attempts to establish a 1-to-1 connection with a server using the
* Listener endpoint from the given Profile description. It shall
* throw a COMM_FAILURE exception if it fails (e.g. if the endpoint
* is unreachable) or a TIMEOUT exception if the given time_out period
* has expired before a connection is established. If the connection
* is successfully established it shall store the used Profile data.
*
*/
public synchronized void connect(org.omg.ETF.Profile server_profile, long time_out)
{
if( ! connected )
{
if (server_profile instanceof IIOPProfile)
{
this.profile = (IIOPProfile) server_profile;
}
else
{
throw new org.omg.CORBA.BAD_PARAM
( "attempt to connect an IIOP connection "
+ "to a non-IIOP profile: " + server_profile.getClass());
}
final IIOPLoopback loopback = getLocalLoopback() ;
if (loopback != null)
{
final IIOPLoopbackInputStream lis = new IIOPLoopbackInputStream() ;
final IIOPLoopbackOutputStream los = new IIOPLoopbackOutputStream() ;
loopback.initLoopback(lis, los) ;
in_stream = lis ;
out_stream = los ;
connected = true;
//for testing purposes
++openTransports;
return;
}
checkSSL();
int retries = noOfRetries;
while( retries >= 0 )
{
try
{
createSocket(time_out);
socket.setTcpNoDelay(true);
if( timeout != 0 )
{
/* re-set the socket timeout */
socket.setSoTimeout( timeout /*note: this is another timeout!*/ );
}
socket.setKeepAlive(keepAlive);
in_stream =
socket.getInputStream();
out_stream =
new BufferedOutputStream( socket.getOutputStream());
if (logger.isInfoEnabled())
{
logger.info("Connected to " + connection_info +
" from local port " +
socket.getLocalPort() +
( this.isSSL() ? " via SSL" : "" ) +
( (timeout == 0) ? "" : " Timeout: " + timeout));
}
connected = true;
//for testing purposes
++openTransports;
return;
}
catch ( IOException c )
{
logger.debug("Exception", c );
//only sleep and print message if we're actually
//going to retry
retries--;
if( retries >= 0 )
{
if (logger.isInfoEnabled())
{
logger.info("Retrying to connect to " +
connection_info );
}
try
{
Thread.sleep( retryInterval );
}
catch( InterruptedException i )
{
}
}
}
catch (TIMEOUT e)
{
//thrown if timeout is expired
profile = null;
use_ssl = false;
ssl_port = -1;
throw e;
}
}
if( retries < 0 )
{
profile = null;
use_ssl = false;
ssl_port = -1;
throw new org.omg.CORBA.TRANSIENT
( "Retries exceeded, couldn't reconnect to " +
connection_info );
}
}
}
private IIOPLoopback getLocalLoopback()
{
final IIOPProfile iiopProfile = (IIOPProfile)profile ;
final List addressList = new ArrayList() ;
addressList.add(iiopProfile.getAddress());
addressList.addAll(iiopProfile.getAlternateAddresses());
final Iterator addressIterator = addressList.iterator() ;
final IIOPLoopbackRegistry registry =
IIOPLoopbackRegistry.getRegistry() ;
while (addressIterator.hasNext())
{
final IIOPAddress address = (IIOPAddress)addressIterator.next() ;
final IIOPLoopback loopback = registry.getLoopback(address) ;
if (loopback != null)
{
return loopback ;
}
}
return null ;
}
/**
* Tries to create a socket connection to any of the addresses in
* the target profile, starting with the primary IIOP address,
* and then any alternate IIOP addresses that have been specified.
*/
private void createSocket(long time_out)
throws IOException
{
List addressList = new ArrayList();
addressList.add(((IIOPProfile)profile).getAddress());
addressList.addAll(((IIOPProfile)profile).getAlternateAddresses());
Iterator addressIterator = addressList.iterator();
Exception exception = null;
socket = null;
while (socket == null && addressIterator.hasNext())
{
try
{
IIOPAddress address = (IIOPAddress)addressIterator.next();
final SocketFactory factory =
(use_ssl) ? getSSLSocketFactory() :
getSocketFactory();
final String ipAddress = address.getIP();
final int port = (use_ssl) ? ssl_port : address.getPort();
if (ipAddress.indexOf(':') == -1)
{
connection_info = ipAddress + ":" + port;
}
else
{
connection_info = "[" + ipAddress + "]:" + port;
}
if (logger.isDebugEnabled())
{
logger.debug("Trying to connect to " + connection_info + " with timeout=" + time_out + ( use_ssl ? " using SSL." : "."));
}
exception = null;
if( time_out > 0 )
{
final int truncatedTimeout = (int) time_out;
if (truncatedTimeout != time_out)
{
logger.warn("timeout might be changed due to conversion from long to int. old value: " + time_out + " new value: " + truncatedTimeout);
}
socket = factory.createSocket(ipAddress, port, truncatedTimeout);
}
else
{
//no timeout --> may hang forever!
socket = factory.createSocket(ipAddress, port);
}
}
catch (Exception e)
{
exception = e;
}
finally
{
if (socket != null && connectionListener.isListenerEnabled())
{
connectionListener.connectionOpened
(
new TCPConnectionEvent
(
this,
socket.getInetAddress().toString(),
socket.getPort(),
socket.getLocalPort(),
getLocalhost()
)
);
}
}
}
if (exception != null)
{
if (exception instanceof SocketTimeoutException)
{
throw new TIMEOUT("connection timeout of " + timeout + " milliseconds expired: " + exception );
}
else if( exception instanceof IOException )
{
throw (IOException) exception;
}
else
{
//not expected, because all used methods just throw IOExceptions or TIMEOUT
//but... never say never ;o)
throw new IOException ( "Unexpected exception occured: " + exception.toString() );
}
}
}
public synchronized void close()
{
if (!connected)
{
return;
}
try
{
if (socket != null)
{
socket.close();
}
//this will cause exceptions when trying to read from
//the streams. Better than "nulling" them.
if( in_stream != null )
{
in_stream.close();
}
if( out_stream != null )
{
out_stream.close();
}
//for testing purposes
--openTransports;
if (logger.isInfoEnabled())
{
logger.info("Client-side TCP transport to " +
connection_info + " closed.");
}
connected = false;
}
catch (IOException ex)
{
if (logger.isDebugEnabled())
{
logger.debug ("Exception when closing the socket", ex);
}
throw handleCommFailure(ex);
}
finally
{
if (socket != null && connectionListener.isListenerEnabled())
{
connectionListener.connectionClosed
(
new TCPConnectionEvent
(
this,
socket.getInetAddress().toString(),
socket.getPort(),
socket.getLocalPort(),
getLocalhost()
)
);
}
}
}
/**
* Check if this client should use SSL when connecting to
* the server described by the 'profile'. The result
* is stored in the private fields use_ssl and ssl_port.
*/
protected void checkSSL()
{
if (!doSupportSSL) return;
int client_required = configuration.getAttributeAsInteger("jacorb.security.ssl.client.required_options", 16);
int client_supported = configuration.getAttributeAsInteger("jacorb.security.ssl.client.supported_options",16);
ssl_port = ((IIOPProfile) profile).getSslPortIfSupported( client_required, client_supported );
use_ssl = ssl_port != -1;
}
public int getSsl_port()
{
return ssl_port;
}
private SocketFactory getSocketFactory()
{
return transportManager.getSocketFactoryManager().getSocketFactory();
}
private SocketFactory getSSLSocketFactory()
{
return transportManager.getSocketFactoryManager().getSSLSocketFactory();
}
}