/* * JacORB - a free Java ORB * * Copyright (C) 2011-2014 Gerald Brose / The JacORB Team. * * 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.nio; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.channels.SocketChannel; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.jacorb.config.Configurable; import org.jacorb.config.Configuration; import org.jacorb.config.ConfigurationException; import org.jacorb.orb.iiop.IIOPAddress; import org.jacorb.orb.iiop.IIOPProfile; import org.jacorb.util.SelectorRequest; import org.jacorb.util.SelectorRequestCallback; import org.omg.CORBA.TIMEOUT; import org.omg.CORBA.TRANSIENT; public class ClientNIOConnection extends NIOConnection implements Configurable { private int noOfRetries = 5; private int retryInterval = 0; public void configure(Configuration configuration) throws ConfigurationException { super.configure(configuration); noOfRetries = configuration.getAttributeAsInteger("jacorb.retries", 5); retryInterval = configuration.getAttributeAsInteger("jacorb.retry_interval", 500); } // time_out is in milliseconds public synchronized void connect(org.omg.ETF.Profile server_profile, long timeout) { long nanoDeadline = (timeout == 0 ? Long.MAX_VALUE : System.nanoTime() + timeout * 1000000); if ( !is_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()); } if (isDebugEnabled) { logger.debug("Trying to establish an NIO client connection with timeout " + timeout); } int retryCount = 0; for (retryCount = 0; retryCount < noOfRetries; retryCount++) { try { connectChannel (nanoDeadline); SocketChannel myChannel; synchronized (this) { myChannel = channel; } if (logger.isInfoEnabled()) { logger.info("Connected to " + connection_info + " from local port " + myChannel.socket().getLocalPort() + ( (timeout == 0) ? "" : " Timeout: " + timeout)); } synchronized (this) { failedWriteAttempts = 0; } setConnected (true); return; } catch (TIMEOUT ex) { profile = null; throw ex; } catch (IOException ex) { if (isDebugEnabled) { logger.debug("Exception connecting {}", ex.getMessage (), ex); } //only sleep and print message if we're actually //going to retry if (retryCount < noOfRetries - 1) { if (logger.isInfoEnabled()) { logger.info("Retrying attempt " + retryCount + " to connect to " + connection_info ); } try { Thread.sleep( retryInterval ); } catch ( InterruptedException i ) { } } } } if (retryCount == noOfRetries) { profile = null; throw new org.omg.CORBA.TRANSIENT ( "Retries exceeded, couldn't reconnect to " + connection_info ); } } } public synchronized void close() { if (!is_connected()) { return; } SocketChannel myChannel; synchronized (this) { myChannel = channel; } silentClose(myChannel); silentClose(in_stream); silentClose(out_stream); setConnected (false); if (logger.isInfoEnabled()) { logger.info("Client-side TCP transport to " + connection_info + " closed."); } } private synchronized void connectChannel (long nanoDeadline) throws IOException { List addressList = new ArrayList(); addressList.add(((IIOPProfile)profile).getAddress()); addressList.addAll(((IIOPProfile)profile).getAlternateAddresses()); Iterator addressIterator = addressList.iterator(); Exception exception = null; while (addressIterator.hasNext()) { SocketChannel myChannel = null; try { myChannel = SocketChannel.open(); myChannel.configureBlocking(false); IIOPAddress address = (IIOPAddress)addressIterator.next(); final String ipAddress = address.getIP(); final int port = address.getPort(); if (ipAddress.indexOf(':') == -1) { connection_info = ipAddress + ":" + port; } else { connection_info = "[" + ipAddress + "]:" + port; } if (isDebugEnabled) { logger.debug("Trying to connect to " + connection_info); } myChannel.connect (new InetSocketAddress (ipAddress, port)); SelectorRequest request = new SelectorRequest (SelectorRequest.Type.CONNECT, myChannel, new ConnectCallback (), nanoDeadline); selectorManager.add (request); request.waitOnCompletion (nanoDeadline); if (request.status == SelectorRequest.Status.IOERROR) { throw new TRANSIENT ("unable to connect"); } else if (request.status == SelectorRequest.Status.EXPIRED || !request.isFinalized()) { throw new TIMEOUT("connection timeout expired"); } else if (request.status == SelectorRequest.Status.FAILED || request.status == SelectorRequest.Status.SHUTDOWN || request.status == SelectorRequest.Status.CLOSED) { // Somethings wrong with SelectorManager. Unwise to proceed throw new IOException ("SelectorManager is corrupted"); } else if (myChannel.isConnected()) { // we are connected synchronized (this) { channel = myChannel; } // if there are multiple endpoints, an earlier // connect attempt may have failed and set the // exception but now we've succeeded we don't want // to throw a spurious exception. exception = null; break; } } catch (Exception ex) { exception = ex; } // TBD: selectorManager.remove (request); if (myChannel != null) { myChannel.close (); } } if (exception != null) { if (exception instanceof TIMEOUT) { throw (TIMEOUT) exception; } else if ( exception instanceof TRANSIENT ) { throw (TRANSIENT) 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() ); } } } private class ConnectCallback extends SelectorRequestCallback { public boolean call (SelectorRequest request) { SocketChannel myChannel = request.channel; if (isDebugEnabled) { logger.debug("Connect callback. Request status: " + request.status.toString()); } try { if (request.status == SelectorRequest.Status.READY) { myChannel.finishConnect (); if (isDebugEnabled) { logger.debug("Connection establishment finished"); } } } catch (Exception ex) { request.setStatus(SelectorRequest.Status.IOERROR); logger.error ("Exception while finishing connection {} ", ex.getMessage (), ex); } return false; } } }