
org.objectweb.dream.protocol.channel.SSLProtocolImpl Maven / Gradle / Ivy
/**
* Dream
* Copyright (C) 2003-2004 INRIA Rhone-Alpes
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Contact: [email protected]
*
* Initial developer(s): Matthieu Leclercq
* Contributor(s): Pierre GARCIA
*/
package org.objectweb.dream.protocol.channel;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
import org.objectweb.dream.IOPushException;
import org.objectweb.dream.PushException;
import org.objectweb.dream.control.activity.Util;
import org.objectweb.dream.control.activity.manager.TaskManager;
import org.objectweb.dream.control.activity.manager.ThreadPoolManager;
import org.objectweb.dream.control.activity.task.AbstractTask;
import org.objectweb.dream.control.activity.task.IllegalTaskException;
import org.objectweb.dream.control.activity.task.thread.ThreadPoolOverflowException;
import org.objectweb.dream.dreamannotation.DreamComponent;
import org.objectweb.dream.dreamannotation.DreamLifeCycle;
import org.objectweb.dream.dreamannotation.DreamMonolog;
import org.objectweb.dream.dreamannotation.util.DreamLifeCycleType;
import org.objectweb.dream.message.ChunkFactoryReference;
import org.objectweb.dream.message.Message;
import org.objectweb.dream.message.MessageManagerType;
import org.objectweb.dream.message.codec.CodecInputOutput;
import org.objectweb.dream.message.codec.MessageCodec;
import org.objectweb.dream.message.codec.SocketCodecInputOutput;
import org.objectweb.dream.protocol.BindException;
import org.objectweb.dream.protocol.ExceptionChunk;
import org.objectweb.dream.protocol.ExportException;
import org.objectweb.dream.protocol.ExportIdentifier;
import org.objectweb.dream.protocol.IPExportIdentifier;
import org.objectweb.dream.protocol.IncomingPush;
import org.objectweb.dream.protocol.InvalidExportIdentifierException;
import org.objectweb.dream.protocol.OutgoingPush;
import org.objectweb.dream.protocol.Protocol;
import org.objectweb.dream.util.Error;
import org.objectweb.dream.util.LoggerUtil;
import org.objectweb.fractal.api.Component;
import org.objectweb.fractal.api.control.IllegalLifeCycleException;
import org.objectweb.fractal.fraclet.annotation.annotations.Attribute;
import org.objectweb.fractal.fraclet.annotation.annotations.Interface;
import org.objectweb.fractal.fraclet.annotation.annotations.Provides;
import org.objectweb.fractal.fraclet.annotation.annotations.Requires;
import org.objectweb.fractal.fraclet.annotation.annotations.Service;
import org.objectweb.util.monolog.api.BasicLevel;
import org.objectweb.util.monolog.api.Logger;
/**
* Implementation of the TCP/IP protocol.
*/
@DreamComponent(controllerDesc = "activeDreamUnstoppablePrimitive")
@Provides(interfaces = { @Interface(name = Protocol.ITF_NAME, signature = SSLProtocol.class) })
public class SSLProtocolImpl implements SSLProtocol, ConnectionManager {
/**
* Variable for debug
*/
// private static final boolean DEFENSIVE_CHECK = true;
/**
* Name associated to the "bind-reply" chunk
*/
protected static final String BIND_REPLY_CHUNK_NAME = "tcpip-bind-reply";
// ------------------------------------------------------------------------
// --
// Services interfaces
// ------------------------------------------------------------------------
// --
/**
* Component reference
*/
@Service
Component weaveableC;
/**
* Logger of the component
*/
@DreamMonolog()
protected Logger logger;
/**
* Chunk factory used to create exception chunk. In the implementation of
* the export/bind pattern, this chunk is used to notify that an exception
* occurred while binding on the server side;
*/
protected ChunkFactoryReference exceptionChunkFactory;
/**
* Set which contains all sessions
*/
protected Set sessions = new HashSet();
/**
* Synchronized Queue which contains available sessions, i.e. sessions that
* can be used by the reader task to receive messages.
*/
protected BlockingQueue availableReadingSessions = new LinkedBlockingQueue();
/**
* Thread pool manager for the reader task
*/
protected ThreadPoolManager threadPoolManager;
/**
* Task in charge of the message receiving via the {@link #messageCodecItf}
*/
protected ReaderTask readerTask = new ReaderTask();
/**
* Map containing {@link ChannelFactory channel factories} identified by
* their {@link IPExportIdentifier} (more precisely by their port because
* the hostname is the same for each {@link ChannelFactory channel factory}
* ).
*/
protected Map exportedChannels = new ConcurrentHashMap();
// ------------------------------------------------------------------------
// ---
// Attribute fields
// ------------------------------------------------------------------------
// ---
/**
* Name of the local host. It should be set to "localhost" in most cases
* except when virtual IP are used
*/
@Attribute(argument = "hostName")
protected String hostName;
/**
* The listening port on which the TCP/IP protocol will wait incoming
* connection for an exported channel.
*/
@Attribute(argument = "port")
protected int port;
// Cannot set a timeout to the SSLSocketFactory!
// @Attribute(argument = "connectionTimeout")
// protected int connectionTimeout;
/**
* The number of connection retries before aborting.
*/
@Attribute(argument = "connectionRetry")
protected short connectionRetry;
/**
* Specify if the Nagle's algorithm is disabled.
*/
@Attribute(argument = "tcpNoDelay")
protected boolean tcpNoDelay;
/**
* the SO_TIMEOUT
parameter of created sockets.
*/
@Attribute(argument = "soTimeout")
protected int soTimeout;
/**
* the SO_LINGER
parameter of created sockets. A negative value
* disable the linger on close.
*/
@Attribute(argument = "soLinger")
protected int soLinger;
/**
* the SO_KEEPALIVE
parameter of created sockets. A {@code
* false} value disable the keeping of an alive connection on the socket.
*/
@Attribute(argument = "soKeepAlive")
protected boolean soKeepAlive;
/**
* The keystore file
*/
@Attribute(argument = "keyStoreFile")
protected String keyStoreFile;
/**
* the keystore password
*/
@Attribute(argument = "keyStorePassword")
protected String keyStorePassword;
/**
* the key password
*/
@Attribute(argument = "keyPassword")
protected String keyPassword;
/**
* The truststore file
*/
@Attribute(argument = "trustStoreFile")
protected String trustStoreFile;
/**
* the truststore password
*/
@Attribute(argument = "trustStorePassword")
protected String trustStorePassword;
// ------------------------------------------------------------------------
// ---
// Client interfaces
// ------------------------------------------------------------------------
// ---
@Requires(name = ConnectionFactoryItf.ITF_NAME)
protected ConnectionFactoryItf connectionFactoryItf;
@Requires(name = "message-manager")
protected MessageManagerType messageManagerItf;
@Requires(name = "message-codec")
protected MessageCodec messageCodecItf;
@Requires(name = "task-manager")
protected TaskManager taskManagerItf;
// ------------------------------------------------------------------------
// ---
// SSL
// ------------------------------------------------------------------------
// ---
/**
* The key manager factory
*/
protected KeyManagerFactory keyManagerFactory;
/**
* The keystore
*/
protected KeyStore keyStore;
/**
* The truststore
*/
protected KeyStore trustStore;
/**
* The trust manager factory
*/
protected TrustManagerFactory trustManagerFactory;
/**
* The SSL context
*/
protected SSLContext sslContext;
/**
* The SSL socket factory
*/
protected SSLSocketFactory sslSocketFactory;
/**
* The SSL server socket factory
*/
protected SSLServerSocketFactory sslServerSocketFactory;
/**
* Is SSL initialized ?
*/
protected Boolean sslInitialized = new Boolean(false);
// ------------------------------------------------------------------------
// ---
// Implementation of the ChannelProtocol interface
// ------------------------------------------------------------------------
// ---
/**
* @see ChannelProtocol#export(ChannelFactory, Map)
*/
public ExportIdentifier export(ChannelFactory channel, Map hints)
throws ExportException {
LoggerUtil.start(this.logger);
int port = this.port;
int portRange = 0;
if (hints != null) {
if (hints.get(PORT) != null) {
port = ((Integer) hints.get(PORT)).intValue();
}
if (hints.get(PORT_RANGE) != null) {
portRange = ((Integer) hints.get(PORT_RANGE)).intValue();
}
}
portRange += port;
ServerSocket serverSocket = null;
Exception exception = null;
while ((port <= portRange) && serverSocket == null) {
exception = null;
try {
serverSocket = this.sslServerSocketFactory.createServerSocket(port, 50, InetAddress
.getByName(this.hostName));
} catch (final IOException e) {
exception = e;
port++;
}
}
if (exception != null) {
throw new ExportException("Unable to open server socket : " + exception.getMessage());
}
final IPExportIdentifier exportIdentifier = new IPExportIdentifier(this.hostName, port);
this.exportedChannels.put(exportIdentifier, channel);
this.connectionFactoryItf.addServerSocket(serverSocket, exportIdentifier);
LoggerUtil.returnAfter(this.logger);
return exportIdentifier;
}
/**
* @see ChannelProtocol#unexport(ExportIdentifier)
*/
public void unexport(ExportIdentifier exportIdentifier) throws InvalidExportIdentifierException {
if (!(exportIdentifier instanceof IPExportIdentifier)) {
throw new InvalidExportIdentifierException(
"this protocol requires a IPExportIdentifier.", exportIdentifier);
}
this.connectionFactoryItf.removeServerSocket((IPExportIdentifier) exportIdentifier);
}
/**
* @param hints
* if "localexportidentifier" key is set, this value is used for
* the (localaddr,localport) of the socket
* @see ChannelProtocol#bind(ExportIdentifier, IncomingPush, Map)
*/
public OutgoingPush bind(ExportIdentifier exportId, IncomingPush toClientPush,
Map hints) throws InvalidExportIdentifierException, BindException {
LoggerUtil.start(this.logger);
// TODO check local binding.
if (!(exportId instanceof IPExportIdentifier)) {
throw new InvalidExportIdentifierException(
"this protocol requires a IPExportIdentifier.", exportId);
}
final IPExportIdentifier identifier = (IPExportIdentifier) exportId;
IPExportIdentifier localExportIdentifier = null;
try {
if (hints != null) {
final Object o = hints.get(LOCAL_EXPORT_IDENTIFIER_KEY);
if (o instanceof IPExportIdentifier) {
this.logger.log(BasicLevel.ERROR, "localExportIdentifier specified : "
+ localExportIdentifier);
localExportIdentifier = (IPExportIdentifier) o;
} else {
this.logger.log(BasicLevel.ERROR, "the local export identifier specified in "
+ "the hints map is not an instance of IPExportIdentifier : "
+ o.getClass().getName());
}
}
} catch (final IllegalStateException e) {
throw new BindException("Unable to open connection to specified export : ", exportId, e);
}
if (localExportIdentifier == null) {
localExportIdentifier = new IPExportIdentifier(this.hostName, 0);
}
Socket clientSocket = null;
try {
clientSocket = this.createConnection(identifier, localExportIdentifier);
} catch (final IOException e) {
throw new BindException("Unable to open connection to specified export : ", exportId, e);
}
Session session;
try {
session = new Session(clientSocket, identifier);
} catch (final IOException e) {
this.silentClose(clientSocket);
throw new BindException("unable to create session for opened socket : ", exportId, e);
}
// wait for acknowledgment message.
this.waitAknowledgment(exportId, session, toClientPush);
this.initSession(session, toClientPush);
LoggerUtil.returnAfter(this.logger);
return session;
}
// ------------------------------------------------------------------------
// ---
// Implementation of the TCPIPProtocol interface
// ------------------------------------------------------------------------
// ---
/**
* @see org.objectweb.dream.protocol.Protocol#createExportIdentifier(Map,
* ExportIdentifier[])
*/
public ExportIdentifier createExportIdentifier(Map info, ExportIdentifier[] next)
throws InvalidExportIdentifierException {
if (next != null && next.length != 0) {
throw new InvalidExportIdentifierException("TCP/IP protocol is a leaf "
+ "in the protocol graph, it cannot have next export identifier");
}
Object o;
if (info == null || (o = info.get(ADDRESS)) == null) {
throw new InvalidExportIdentifierException("Can't find address in info map");
}
String hostName;
if (o instanceof String) {
hostName = (String) o;
} else if (o instanceof InetAddress) {
hostName = ((InetAddress) o).getCanonicalHostName();
} else {
throw new InvalidExportIdentifierException(
"Invalid address object in info map, must be an InetAddress or a String");
}
o = info.get(PORT);
if (o == null) {
throw new InvalidExportIdentifierException("Can't find port in info map");
}
if (!(o instanceof Number)) {
throw new InvalidExportIdentifierException(
"Invalid port object in info map, must be a Number");
}
final int port = ((Number) o).intValue();
return this.createExportIdentifier(hostName, port);
}
/**
* @see TCPIPProtocol#createExportIdentifier(String, int)
*/
public IPExportIdentifier createExportIdentifier(String hostName, int port) {
return new IPExportIdentifier(hostName, port);
}
// ------------------------------------------------------------------------
// ---
// Implementation of the ConnectionManager interface
// ------------------------------------------------------------------------
// ---
/**
* @see ConnectionManager#newConnection(Socket, IPExportIdentifier)
*/
public void newConnection(Socket socket, IPExportIdentifier identifier) {
LoggerUtil.start(this.logger);
final ChannelFactory channel = this.exportedChannels.get(identifier);
final Message message = this.messageManagerItf.createMessage();
final ExceptionChunk chunk = this.messageManagerItf.createChunk(this.exceptionChunkFactory);
this.messageManagerItf.addChunk(message, BIND_REPLY_CHUNK_NAME, chunk);
Session session;
try {
session = new Session(socket, identifier);
} catch (final IOException e) {
this.silentClose(socket);
this.logger
.log(BasicLevel.ERROR, "Unable to create session for new incoming socket", e);
return;
}
IncomingPush incomingPush = null;
try {
incomingPush = channel.instantiate(session);
} catch (final IOException e) {
chunk.setException(e);
this.logger.log(BasicLevel.DEBUG,
"Exception caught while instantiating channel factory", e);
}
// send the bind reply of the export/bind pattern
try {
session.outgoingPush(message);
} catch (final IOPushException e) {
this.logger.log(BasicLevel.ERROR, "Unable to send reply to client, close socket", e);
this.silentClose(session);
return;
}
if (incomingPush != null) {
this.initSession(session, incomingPush);
}
LoggerUtil.end(this.logger);
}
// ------------------------------------------------------------------------
// ---
// Inner Class
// ------------------------------------------------------------------------
// ---
protected class Session implements OutgoingPush {
boolean closed = false;
Socket socket;
CodecInputOutput codecInputOutput = new SocketCodecInputOutput();
IPExportIdentifier exportIdentifier;
IncomingPush upperIncomingPushItf;
Session(Socket socket, IPExportIdentifier exportIdentifier) throws IOException {
this.socket = socket;
this.exportIdentifier = exportIdentifier;
this.codecInputOutput.setInput(socket.getInputStream());
this.codecInputOutput.setOutput(socket.getOutputStream());
}
synchronized void closeSession() throws IOException {
LoggerUtil.call(SSLProtocolImpl.this.logger);
this.closed = true;
this.codecInputOutput.close();
LoggerUtil.end(SSLProtocolImpl.this.logger);
}
// --------------------------------------------------------------------
// -----
// Implementation of the OutgoingPush interface
// --------------------------------------------------------------------
// -----
/**
* @see OutgoingPush#outgoingPush(Message)
*/
public synchronized void outgoingPush(Message message) throws IOPushException {
LoggerUtil.start(SSLProtocolImpl.this.logger);
if (this.closed) {
throw new IOPushException("Session closed");
}
try {
SSLProtocolImpl.this.messageCodecItf.encode(this.codecInputOutput, message);
} catch (final IOException e) {
if (!this.closed) {
SSLProtocolImpl.this.logger.log(BasicLevel.INFO,
"An error occurs while writing message on TCP/IP socket", e);
SSLProtocolImpl.this.sessionError(this, e);
}
throw new IOPushException("unable to send message", e);
}
SSLProtocolImpl.this.messageManagerItf.deleteMessage(message);
LoggerUtil.end(SSLProtocolImpl.this.logger);
}
/**
* @see OutgoingPush#outgoingClose(IncomingPush)
*/
public void outgoingClose(IncomingPush incomingPush) throws IOException {
this.closeSession();
SSLProtocolImpl.this.sessions.remove(this);
}
/**
* Nice format for output
*/
@Override
public String toString() {
String res = "";
if (this.exportIdentifier == null) {
res = "AnonymousSSLSession@" + Integer.toHexString(this.hashCode()) + "(";
} else {
res = "SSLSession@" + Integer.toHexString(this.hashCode()) + "(id:"
+ this.exportIdentifier + ", localSocket(" + this.socket.getLocalAddress()
+ ":" + this.socket.getLocalPort() + "), remoteSocket("
+ this.socket.getInetAddress() + ":" + this.socket.getPort() + "), ";
}
res = "State : " + (this.closed ? "closed" : "open") + ")";
return res;
}
}
protected class ReaderTask extends AbstractTask {
public ReaderTask() {
super("SSL/IP reader task");
}
// --------------------------------------------------------------------
// -----
// Implementation of the Task interface
// --------------------------------------------------------------------
// -----
/**
* @see org.objectweb.dream.control.activity.task.Task#execute(Object)
*/
public Object execute(Object hints) throws InterruptedException {
Session session;
session = SSLProtocolImpl.this.availableReadingSessions.poll(5, TimeUnit.SECONDS);
if (session == null) {
SSLProtocolImpl.this.logger.log(BasicLevel.INFO,
"No available session for the reader task");
return STOP_EXECUTING;
}
if (session.closed) {
SSLProtocolImpl.this.logger.log(BasicLevel.INFO, session
+ " is closed, this task will be stopped");
return STOP_EXECUTING;
}
Message message;
try {
message = SSLProtocolImpl.this.messageCodecItf.decode(session.codecInputOutput);
} catch (final IOException e) {
synchronized (this) {
// test if the session was properly closed and causes this
// exception (= closeSession() method called)
if (!session.closed) {
SSLProtocolImpl.this.logger.log(BasicLevel.INFO,
"An error occurs while reading message on SSL socket", e);
SSLProtocolImpl.this.sessionError(session, e);
}
}
return STOP_EXECUTING;
}
try {
session.upperIncomingPushItf.incomingPush(message);
SSLProtocolImpl.this.logger.log(BasicLevel.DEBUG,
"Sending a message to the upper level protocol");
} catch (final PushException e) {
SSLProtocolImpl.this.logger.log(BasicLevel.ERROR,
"Exception catch while pushing incoming message. delete message.", e);
SSLProtocolImpl.this.messageManagerItf.deleteMessage(message);
}
SSLProtocolImpl.this.availableReadingSessions.put(session);
return EXECUTE_AGAIN;
}
/**
* @see org.objectweb.dream.control.activity.task.Task#registered(Object)
*/
@Override
public void registered(Object controlItf) {
SSLProtocolImpl.this.setThreadPoolManager((ThreadPoolManager) controlItf);
}
}
// ------------------------------------------------------------------------
// ---
// Utility methods
// ------------------------------------------------------------------------
// ---
/**
* Close the session and ignore potential IOException
*
* @param session
* the session to close
*/
protected final void silentClose(Session session) {
try {
session.closeSession();
} catch (final IOException e) {
// ignore
this.logger.log(BasicLevel.DEBUG, "An error occured while closing " + session + " : "
+ e.getMessage());
}
}
/**
* Close the socket and ignore potential IOException
*
* @param socket
* the socket to close
*/
protected final void silentClose(Socket socket) {
LoggerUtil.call(this.logger);
try {
socket.shutdownInput();
socket.shutdownOutput();
socket.close();
} catch (final IOException e) {
// ignore
this.logger.log(BasicLevel.DEBUG, "An error occured while closing a socket : "
+ e.getMessage());
}
}
/**
* @param session
* @param upperIncomingPush
* incoming interface of the upper session
* @return
*/
protected synchronized Session initSession(Session session, IncomingPush upperIncomingPush) {
LoggerUtil.start(this.logger);
session.upperIncomingPushItf = upperIncomingPush;
synchronized (this.sessions) {
this.sessions.add(session);
if (session.upperIncomingPushItf != null) {
try {
this.availableReadingSessions.put(session);
} catch (final InterruptedException e1) {
Error.bug(this.logger, e1);
}
if (this.threadPoolManager != null) {
try {
this.logger.log(BasicLevel.DEBUG, "add a thread in thread pool");
this.threadPoolManager.addThread(this.readerTask);
} catch (final Exception e) {
Error.bug(this.logger, e);
}
}
}
LoggerUtil.returnAfter(this.logger);
}
return session;
}
protected void setThreadPoolManager(ThreadPoolManager threadPoolManager) {
this.threadPoolManager = threadPoolManager;
synchronized (this.sessions) {
for (final Session s : this.sessions) {
try {
this.logger.log(BasicLevel.DEBUG, "add a thread in thread pool for " + s);
threadPoolManager.addThread(this.readerTask);
this.availableReadingSessions.put(s);
} catch (final ThreadPoolOverflowException e) {
this.logger.log(BasicLevel.WARN, "Unable to add reader thread", e);
} catch (final IllegalTaskException e) {
Error.bug(this.logger, e);
} catch (final InterruptedException e) {
Error.bug(this.logger, e);
}
}
}
}
protected void sessionError(Session session, Exception exception) {
this.sessions.remove(session);
this.silentClose(session);
if (session.upperIncomingPushItf != null) {
// session is a server session, inform channel.
session.upperIncomingPushItf.incomingClosed(session, exception);
}
}
/**
* Try to create a connection toward the destination specified via the
* export identifier. the number of try depends on the value of the
* connectionRetry
fractal attribute.
*
* @param exportIdentifier
* identifier the destination
* @param localExportIdentifier
* @return the created socket
* @throws IOException
* if an error occured while connecting
* @throws CertificateException
* @throws NoSuchAlgorithmException
* @throws KeyStoreException
* @throws UnrecoverableKeyException
* @throws KeyManagementException
*/
protected Socket createConnection(IPExportIdentifier exportIdentifier,
IPExportIdentifier localExportIdentifier) throws IOException {
IOException exception = null;
Socket clientSocket = null;
synchronized (this.sslInitialized) {
if (!this.sslInitialized) {
throw new IllegalStateException("SSLProtocol is not initialized");
}
}
for (int i = 0; i <= this.connectionRetry; i++) {
try {
this.logger.log(BasicLevel.DEBUG, "try to open a SSL connection");
if (localExportIdentifier == null) {
clientSocket = this.sslSocketFactory.createSocket(exportIdentifier
.getHostName(), exportIdentifier.getPort());
} else {
clientSocket = this.sslSocketFactory.createSocket(exportIdentifier
.getHostName(), exportIdentifier.getPort(), InetAddress
.getByName(localExportIdentifier.getHostName()), localExportIdentifier
.getPort());
}
this.logger.log(BasicLevel.DEBUG, "connected to " + exportIdentifier.getHostName()
+ ":" + exportIdentifier.getPort());
// TODO: Check if the setting of the parameters is working after
// having connected the SSL socket
this.setSocketOption(clientSocket);
break;
} catch (final IOException exc) {
exception = exc;
this.logger.log(BasicLevel.INFO, "connection failed on address "
+ exportIdentifier.getHostName() + ":" + exportIdentifier.getPort()
+ ". Another attempt will be done at " + i * 250 + " ms");
try {
Thread.sleep(i * 250);
} catch (final InterruptedException ignored) {
break;
}
}
}
if (exception != null) {
throw exception;
} else {
return clientSocket;
}
}
protected void setSocketOption(Socket sock) throws IOException {
sock.setTcpNoDelay(this.tcpNoDelay);
sock.setSoTimeout(this.soTimeout);
sock.setKeepAlive(this.soKeepAlive);
if (this.soLinger >= 0) {
sock.setSoLinger(true, this.soLinger);
} else {
sock.setSoLinger(false, 0);
}
if (this.logger.isLoggable(BasicLevel.DEBUG)) {
this.logger.log(BasicLevel.DEBUG, "Options for socket (" + sock.getInetAddress() + ","
+ sock.getPort() + ") : tcpNoDelay = " + sock.getTcpNoDelay() + " soLinger = "
+ sock.getSoLinger() + " soTimeout = " + sock.getSoTimeout()
+ " soKeepAlive = " + sock.getKeepAlive() + " sendBufferSize = "
+ sock.getSendBufferSize());
}
}
// -------------------------------------------------------------------------
// Dream Components Lifecycle Methods
// -------------------------------------------------------------------------
/**
* Method executed during the first start of the component
*/
@DreamLifeCycle(on = DreamLifeCycleType.FIRST_START)
synchronized protected void beforeFirstStart(Component componentItf)
throws IllegalLifeCycleException {
this.logger.log(BasicLevel.DEBUG,
"First start of the component : SSL initialisation + SSL reader task added");
try {
synchronized (this.sslInitialized) {
if (!this.sslInitialized) {
this.initSSL();
}
}
final Map hints = new HashMap();
hints.put("thread", "pool");
Util.addTask(componentItf, this.readerTask, hints);
} catch (final Exception e) {
this.logger.log(BasicLevel.FATAL, e);
throw new IllegalLifeCycleException("Failed to Prepare first start. Caused : "
+ e.getClass().getSimpleName() + " - " + e.getMessage());
}
this.exceptionChunkFactory = this.messageManagerItf.getChunkFactory(ExceptionChunk.class);
}
/**
* @see org.objectweb.dream.control.lifecycle.PrepareStopLifeCycleController#
* prepareStopFc()
*/
@DreamLifeCycle(on = DreamLifeCycleType.PREPARE_TO_STOP)
public void prepareStopFc() throws IllegalLifeCycleException {
synchronized (this.sessions) {
this.logger.log(BasicLevel.DEBUG,
"Prepare stop of the component : Close every opened SSL sockets");
for (final Session session : this.sessions) {
this.logger.log(BasicLevel.DEBUG, "Going to close " + session);
this.silentClose(session);
}
this.sessions.clear();
}
}
/**
* @see TCPIPProtocolImplAttributeController#setHostName(String)
*/
public void setHostName(String hostname) throws UnknownHostException {
if (hostname.trim().equalsIgnoreCase("localhost")) {
this.hostName = InetAddress.getLocalHost().getHostName();
} else {
// this call make a check on the given parameter
this.hostName = InetAddress.getByName(hostname).getHostName();
}
}
/**
* Initialize the SSL environment.
*
* @throws KeyStoreException
* @throws NoSuchAlgorithmException
* @throws CertificateException
* @throws FileNotFoundException
* @throws IOException
* @throws KeyManagementException
* @throws UnrecoverableKeyException
*/
private synchronized void initSSL() throws KeyStoreException, NoSuchAlgorithmException,
CertificateException, FileNotFoundException, IOException, KeyManagementException,
UnrecoverableKeyException {
this.keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory
.getDefaultAlgorithm());
this.keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
this.keyStore.load(new FileInputStream(this.keyStoreFile), this.keyStorePassword
.toCharArray());
this.trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
this.trustStore.load(new FileInputStream(this.trustStoreFile), this.trustStorePassword
.toCharArray());
this.keyManagerFactory.init(this.keyStore, this.keyPassword.toCharArray());
this.trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory
.getDefaultAlgorithm());
this.trustManagerFactory.init(this.trustStore);
this.sslContext = SSLContext.getInstance("TLS");
this.sslContext.init(this.keyManagerFactory.getKeyManagers(), this.trustManagerFactory
.getTrustManagers(), null);
this.sslSocketFactory = this.sslContext.getSocketFactory();
this.sslServerSocketFactory = this.sslContext.getServerSocketFactory();
this.sslInitialized = true;
}
/**
* Wait the bind acknowledgment.
*
* @param exportId
* @param session
* @param toClientPush
* @throws BindException
*/
private void waitAknowledgment(ExportIdentifier exportId, Session session,
IncomingPush toClientPush) throws BindException {
Message message;
try {
message = this.messageCodecItf.decode(session.codecInputOutput);
} catch (final IOException e) {
this.silentClose(session);
throw new BindException("Unable to receive acknowledgment message", exportId, e);
}
ExceptionChunk chunk = this.messageManagerItf.getChunk(message, BIND_REPLY_CHUNK_NAME);
if (chunk == null) {
if ((chunk = this.messageManagerItf.getChunk(message,
ChannelFactory.BIND_REPLY_CHUNK_NAME)) != null) {
// it's an BIND REPLY CHUNK for the upper protocol
try {
toClientPush.incomingPush(message);
} catch (final PushException e) {
Error.error("First received message on socket does contain "
+ "a bind-reply chunk for the channel factory "
+ "and it cannot be transmitted to it", this.logger);
}
// recursive call
this.waitAknowledgment(exportId, session, toClientPush);
} else {
this.silentClose(session);
Error.error("First received message on socket does not contain "
+ "acknowledgment chunk", this.logger);
}
} else {
if (chunk.getException() != null) {
throw new BindException("Remote exception", exportId, chunk.getException());
}
}
this.messageManagerItf.deleteMessage(message);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy