001/**
002 * Copyright (C) 2014  Universidade de Aveiro, DETI/IEETA, Bioinformatics Group - http://bioinformatics.ua.pt/
003 *
004 * This file is part of Dicoogle/dicoogle.
005 *
006 * Dicoogle/dicoogle is free software: you can redistribute it and/or modify
007 * it under the terms of the GNU General Public License as published by
008 * the Free Software Foundation, either version 3 of the License, or
009 * (at your option) any later version.
010 *
011 * Dicoogle/dicoogle is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
014 * GNU General Public License for more details.
015 *
016 * You should have received a copy of the GNU General Public License
017 * along with Dicoogle.  If not, see <http://www.gnu.org/licenses/>.
018 */
019/*
020 * To change this template, choose Tools | Templates
021 * and open the template in the editor.
022 */
023
024package pt.ua.dicoogle.rGUI;
025
026import java.io.IOException;
027import java.io.Serializable;
028import java.net.InetSocketAddress;
029import java.net.Socket;
030import java.net.SocketAddress;
031import java.net.SocketException;
032import java.nio.channels.Channel;
033import java.nio.channels.SelectionKey;
034import java.nio.channels.Selector;
035import java.nio.channels.SocketChannel;
036import java.rmi.RMISecurityManager;
037import java.rmi.server.RMIClientSocketFactory;
038import java.rmi.server.RMISocketFactory;
039import java.util.ArrayList;
040import java.util.List;
041import java.util.Set;
042
043@Deprecated
044public class MultihomeRMIClientSocketFactory
045        implements RMIClientSocketFactory, Serializable {
046    private static final long serialVersionUID = 7033753601964541325L;
047
048    private final RMIClientSocketFactory factory;
049
050    public MultihomeRMIClientSocketFactory() {
051        this.factory = RMISocketFactory.getSocketFactory();
052       /* System.setProperty("java.security.policy","java.policy");
053        System.setProperty("java.rmi.server.codebase", "file:/c:/pluginClasses/");*/
054    }
055
056    @Override
057    public Socket createSocket(String hostString, int port) throws IOException {
058        //System.setProperty("java.security.policy", "my.policy");
059        final String[] hosts = hostString.split("!");
060        final int nhosts = hosts.length;
061        
062        if (nhosts < 2)
063            return factory().createSocket(hostString, port);
064
065        List<IOException> exceptions = new ArrayList<IOException>();
066        Selector selector = Selector.open();
067        for (String host : hosts) {
068            SocketChannel channel = SocketChannel.open();
069            channel.configureBlocking(false);
070            channel.register(selector, SelectionKey.OP_CONNECT);
071            SocketAddress addr = new InetSocketAddress(host, port);
072
073            try{
074                channel.connect(addr);
075            }catch(SocketException e){
076                //System.err.println("Sockect Exception: "+ e.getMessage() + ", Host: " + host + ", Port: "+ port);
077            }
078        }
079        SocketChannel connectedChannel = null;
080
081        connect:
082        while (true) {
083            if (selector.keys().isEmpty()) {
084                throw new IOException("Connection failed for " + hostString +
085                        ": " + exceptions);
086            }
087            selector.select();  // you can add a timeout parameter in millseconds
088            Set<SelectionKey> keys = selector.selectedKeys();
089            if (keys.isEmpty()) {
090                throw new IOException("Selection keys unexpectedly empty for " +
091                        hostString + "[exceptions: " + exceptions + "]");
092            }
093            for (SelectionKey key : keys) {
094                SocketChannel channel = (SocketChannel) key.channel();
095                key.cancel();
096                try {
097                    channel.configureBlocking(true);
098                    channel.finishConnect();
099                    connectedChannel = channel;
100                    break connect;
101                } catch (IOException e) {
102                    exceptions.add(e);
103                }
104            }
105        }
106
107        assert connectedChannel != null;
108
109        // Close the channels that didn't connect
110        for (SelectionKey key : selector.keys()) {
111            Channel channel = key.channel();
112            if (channel != connectedChannel)
113                channel.close();
114        }
115
116        final Socket socket = connectedChannel.socket();
117        if (factory == null && RMISocketFactory.getSocketFactory() == null)
118            return socket;
119
120        // We've determined that we can connect to this host but we didn't use
121        // the right factory so we have to reconnect with the factory.
122        String host = socket.getInetAddress().getHostAddress();
123        socket.close();
124        return factory().createSocket(host, port);
125    }
126
127    private RMIClientSocketFactory factory() {
128        if (factory != null)
129            return factory;
130        RMIClientSocketFactory f = RMISocketFactory.getSocketFactory();
131        if (f != null)
132            return f;
133        return RMISocketFactory.getDefaultSocketFactory();
134    }
135
136    // Thanks to "km" for the reminder that I need these:
137    @Override
138    public boolean equals(Object x) {
139        if (x.getClass() != this.getClass())
140            return false;
141        MultihomeRMIClientSocketFactory f = (MultihomeRMIClientSocketFactory) x;
142        return ((factory == null) ?
143                (f.factory == null) :
144                (factory.equals(f.factory)));
145    }
146
147    @Override
148    public int hashCode() {
149        int h = getClass().hashCode();
150        if (factory != null)
151            h += factory.hashCode();
152        return h;
153    }
154}