Java tutorial
/* * Copyright 2009 Thomas Bocek * * 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 net.tomp2p.connection; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.util.concurrent.DefaultThreadFactory; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.GenericFutureListener; import java.io.IOException; import java.net.InetAddress; import java.security.KeyPair; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import net.tomp2p.futures.BaseFutureAdapter; import net.tomp2p.futures.FutureDone; import net.tomp2p.peers.Number160; import net.tomp2p.peers.PeerAddress; import net.tomp2p.peers.PeerSocketAddress; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Creates a peer and listens to incoming connections. The result of creating * this class is the connection bean and the peer bean. While the connection * bean holds information that can be shared, the peer bean holds information * that is unique for each peer. * * @author Thomas Bocek * */ public class PeerCreator { private static final Logger LOG = LoggerFactory.getLogger(PeerCreator.class); private final ConnectionBean connectionBean; private final PeerBean peerBean; private final List<PeerCreator> childConnections = new ArrayList<PeerCreator>(); private final EventLoopGroup workerGroup; private final EventLoopGroup bossGroup; private final boolean master; private final FutureDone<Void> futureServerDone = new FutureDone<Void>(); /** * Creates a master peer and starts UPD and TCP channels. * * @param p2pId * The id of the network * @param peerId * The id of this peer * @param keyPair * The key pair or null * @param channelServerConfiguration * The server configuration to create the channel server that is * used for listening for incoming connections * @param channelClientConfiguration * The client side configuration * @param timer * The executor service * @param sendBehavior * The sending behavior for direct messages * @throws IOException * If the startup of listening to connections failed */ public PeerCreator(final int p2pId, final Number160 peerId, final KeyPair keyPair, final ChannelServerConfiguration channelServerConfiguration, final ChannelClientConfiguration channelClientConfiguration, final ScheduledExecutorService timer, SendBehavior sendBehavior) throws IOException { //peer bean peerBean = new PeerBean(keyPair); PeerAddress self = findPeerAddress(peerId, channelClientConfiguration, channelServerConfiguration); peerBean.serverPeerAddress(self); LOG.info("Visible address to other peers: {}", self); //start server workerGroup = new NioEventLoopGroup(0, new DefaultThreadFactory(ConnectionBean.THREAD_NAME + "worker-client/server - ")); bossGroup = new NioEventLoopGroup(2, new DefaultThreadFactory(ConnectionBean.THREAD_NAME + "boss - ")); Dispatcher dispatcher = new Dispatcher(p2pId, peerBean, channelServerConfiguration.heartBeatMillis()); final ChannelServer channelServer = new ChannelServer(bossGroup, workerGroup, channelServerConfiguration, dispatcher, peerBean.peerStatusListeners(), timer); //connection bean Sender sender = new Sender(peerId, peerBean.peerStatusListeners(), channelClientConfiguration, dispatcher, sendBehavior, peerBean); Reservation reservation = new Reservation(workerGroup, channelClientConfiguration); connectionBean = new ConnectionBean(p2pId, dispatcher, sender, channelServer, reservation, channelClientConfiguration, timer); this.master = true; } /** * Creates a slave peer that will attach itself to a master peer. * * @param parent * The parent peer * @param peerId * The id of this peer * @param keyPair * The key pair or null */ public PeerCreator(final PeerCreator parent, final Number160 peerId, final KeyPair keyPair) { parent.childConnections.add(this); this.workerGroup = parent.workerGroup; this.bossGroup = parent.bossGroup; this.connectionBean = parent.connectionBean; this.peerBean = new PeerBean(keyPair); PeerAddress self = parent.peerBean().serverPeerAddress().changePeerId(peerId); this.peerBean.serverPeerAddress(self); this.master = false; } /** * Shutdown the peer. If the peer is a master, then also the connections and * the server will be closed, otherwise its just de-registering. * * @return The future when the shutdown is complete */ public FutureDone<Void> shutdown() { if (master) { LOG.debug("shutdown in progress..."); } // de-register in dispatcher connectionBean.dispatcher().removeIoHandler(peerBean().serverPeerAddress().peerId(), peerBean().serverPeerAddress().peerId()); // shutdown running tasks for this peer if (peerBean.maintenanceTask() != null) { peerBean.maintenanceTask().shutdown(); } // shutdown all children if (!master) { for (PeerCreator peerCreator : childConnections) { peerCreator.shutdown(); } return futureServerDone.done(); } // shutdown the timer connectionBean.timer().shutdown(); LOG.debug("starting shutdown done in client..."); connectionBean.reservation().shutdown().addListener(new BaseFutureAdapter<FutureDone<Void>>() { @Override public void operationComplete(final FutureDone<Void> future) throws Exception { connectionBean.channelServer().shutdown().addListener(new BaseFutureAdapter<FutureDone<Void>>() { @Override public void operationComplete(final FutureDone<Void> future) throws Exception { shutdownNetty(); } }); } }); // this is blocking return futureServerDone; } @SuppressWarnings({ "unchecked", "rawtypes" }) private void shutdownNetty() { workerGroup.shutdownGracefully(0, 0, TimeUnit.SECONDS).addListener(new GenericFutureListener() { @Override public void operationComplete(final Future future) throws Exception { LOG.debug("shutdown done in client / workerGroup..."); bossGroup.shutdownGracefully(0, 0, TimeUnit.SECONDS).addListener(new GenericFutureListener() { @Override public void operationComplete(final Future future) throws Exception { LOG.debug("shutdown done in client / bossGroup..."); futureServerDone.done(); } }); } }); } /** * @return The bean that holds information that is unique for all peers */ public PeerBean peerBean() { return peerBean; } /** * @return The bean that holds information that may be shared amoung peers */ public ConnectionBean connectionBean() { return connectionBean; } /** * Creates the {@link PeerAddress} based on the network discovery that was * done in * {@link #ChannelServer(Bindings, int, int, ChannelServerConfiguration)}. * * @param peerId * The id of this peer * @return The peer address of this peer * @throws IOException * If the address could not be determined */ private static PeerAddress findPeerAddress(final Number160 peerId, final ChannelClientConfiguration channelClientConfiguration, final ChannelServerConfiguration channelServerConfiguration) throws IOException { final DiscoverResults discoverResults = DiscoverNetworks .discoverInterfaces(channelClientConfiguration.bindings()); final String status = discoverResults.status(); if (LOG.isInfoEnabled()) { LOG.info("Status of external search: " + status); } //this is just a guess and will be changed once discovery is done InetAddress outsideAddress = discoverResults.foundAddress(); if (outsideAddress == null) { throw new IOException("Not listening to anything. Maybe your binding information is wrong."); } final PeerSocketAddress peerSocketAddress = new PeerSocketAddress(outsideAddress, channelServerConfiguration.ports().tcpPort(), channelServerConfiguration.ports().udpPort()); final PeerAddress self = new PeerAddress(peerId, peerSocketAddress, channelServerConfiguration.isBehindFirewall(), channelServerConfiguration.isBehindFirewall(), false, false, false, PeerAddress.EMPTY_PEER_SOCKET_ADDRESSES); return self; } }