Java tutorial
/* * Copyright 2014 Napolov Dmitry * * 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 org.aotorrent.client; import com.google.common.collect.Maps; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.logging.LogLevel; import io.netty.handler.logging.LoggingHandler; import io.netty.util.AttributeKey; import org.aotorrent.common.Torrent; import org.aotorrent.common.TorrentEngine; import org.aotorrent.common.bencode.InvalidBEncodingException; import org.apache.commons.codec.binary.Hex; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.annotation.Nullable; import java.io.IOException; import java.net.InetSocketAddress; import java.util.Map; /** * Project: AOTorrent * User: dmitry * Date: 3/13/14. */ public class TorrentClient implements Runnable { private static final Logger LOGGER = LoggerFactory.getLogger(TorrentClient.class); private final Map<String, TorrentEngine> torrentEngines = Maps.newHashMap(); private final InetSocketAddress localSocketAddress; private boolean isRunning; private EventLoopGroup workerGroup; private EventLoopGroup bossGroup; private Thread thread; public TorrentClient(InetSocketAddress localSocketAddress) { this.localSocketAddress = localSocketAddress; } @Override public void run() { // Configure the server. try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG, 100).handler(new LoggingHandler(LogLevel.INFO)) .childHandler(new InboundChannelInitializer(this)) .childAttr(AttributeKey.valueOf("torrentEngines"), torrentEngines); // Start the server. ChannelFuture f = b.bind(localSocketAddress.getPort()).sync(); // Wait until the server socket is closed. f.channel().closeFuture().sync(); } catch (InterruptedException e) { LOGGER.info("Interrupted"); } finally { // Shut down all event loops to terminate all threads. bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } public TorrentEngine addTorrent(String torrentFilePath, String downloadPath) throws IOException, InvalidBEncodingException { if (!isRunning) { throw new IllegalStateException("Client should be started before adding torrents"); } Torrent torrent = new Torrent(torrentFilePath, downloadPath); final String infoHash = Hex.encodeHexString(torrent.getInfoHash()); if (torrentEngines.containsKey(infoHash)) { throw new IllegalStateException("This torrent already exist in engine"); } TorrentEngine torrentEngine = new TorrentEngine(torrent, localSocketAddress, workerGroup); torrentEngines.put(infoHash, torrentEngine); torrentEngine.init(); return torrentEngine; } @Nullable public TorrentEngine getEngine(String infoHash) { return torrentEngines.get(infoHash); } public void start() { bossGroup = new NioEventLoopGroup(); workerGroup = new NioEventLoopGroup(); thread = new Thread(this); thread.start(); isRunning = true; } public void stop() { this.isRunning = false; for (TorrentEngine torrentEngine : torrentEngines.values()) { torrentEngine.shutdown(); } thread.interrupt(); } @Override public String toString() { return "TorrentClient{" + "TorrentEngines=" + torrentEngines.values() + '}'; } public boolean isAlive() { return thread.isAlive(); } }