001package com.github.theholywaffle.teamspeak3; 002 003/* 004 * #%L 005 * TeamSpeak 3 Java API 006 * %% 007 * Copyright (C) 2015 Bert De Geyter 008 * %% 009 * Permission is hereby granted, free of charge, to any person obtaining a copy 010 * of this software and associated documentation files (the "Software"), to deal 011 * in the Software without restriction, including without limitation the rights 012 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 013 * copies of the Software, and to permit persons to whom the Software is 014 * furnished to do so, subject to the following conditions: 015 * 016 * The above copyright notice and this permission notice shall be included in 017 * all copies or substantial portions of the Software. 018 * 019 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 020 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 021 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 022 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 023 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 024 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 025 * THE SOFTWARE. 026 * #L% 027 */ 028 029import com.github.theholywaffle.teamspeak3.api.Callback; 030import com.github.theholywaffle.teamspeak3.api.exception.TS3ConnectionFailedException; 031import com.github.theholywaffle.teamspeak3.commands.Command; 032 033import java.io.IOException; 034import java.net.Socket; 035import java.util.Collections; 036import java.util.LinkedHashMap; 037import java.util.Map; 038import java.util.Queue; 039import java.util.concurrent.ConcurrentLinkedQueue; 040 041public class QueryIO { 042 043 private final Socket socket; 044 private final SocketReader socketReader; 045 private final SocketWriter socketWriter; 046 private final KeepAliveThread keepAlive; 047 048 private final Queue<Command> commandQueue; 049 private final Map<Command, Callback> callbackMap; 050 051 QueryIO(TS3Query query, TS3Config config) { 052 commandQueue = new ConcurrentLinkedQueue<>(); 053 callbackMap = Collections.synchronizedMap(new LinkedHashMap<Command, Callback>(16)); 054 055 Socket tmpSocket = null; 056 try { 057 tmpSocket = new Socket(config.getHost(), config.getQueryPort()); 058 socket = tmpSocket; 059 socketReader = new SocketReader(query, this); 060 socketWriter = new SocketWriter(this, config.getFloodRate().getMs()); 061 keepAlive = new KeepAliveThread(socketWriter, query.getAsyncApi()); 062 } catch (IOException e) { 063 // Clean up resources and fail 064 if (tmpSocket != null) { 065 try { 066 tmpSocket.close(); 067 } catch (IOException ignored) { 068 } 069 } 070 071 throw new TS3ConnectionFailedException(e); 072 } 073 074 // From here on: all resources have been initialized and are non-null 075 socketReader.start(); 076 socketWriter.start(); 077 keepAlive.start(); 078 } 079 080 public void continueFrom(QueryIO io) { 081 if (io == null || io.commandQueue.isEmpty()) return; 082 083 callbackMap.putAll(io.callbackMap); 084 Command lastSent = io.commandQueue.poll(); 085 commandQueue.add(lastSent.reset()); 086 commandQueue.addAll(io.commandQueue); 087 088 io.commandQueue.clear(); 089 io.callbackMap.clear(); 090 } 091 092 public void disconnect() { 093 keepAlive.interrupt(); 094 socketWriter.interrupt(); 095 socketReader.interrupt(); 096 097 try { 098 keepAlive.join(); 099 socketWriter.join(); 100 socketReader.join(); 101 } catch (final InterruptedException e) { 102 // Restore the interrupt for the caller 103 Thread.currentThread().interrupt(); 104 } 105 106 try { 107 socket.close(); 108 } catch (IOException ignored) { 109 } 110 } 111 112 public void queueCommand(Command command, Callback callback) { 113 if (command == null) throw new NullPointerException("Command cannot be null!"); 114 115 if (callback != null) { 116 callbackMap.put(command, callback); 117 } 118 commandQueue.add(command); 119 } 120 121 public Socket getSocket() { 122 return socket; 123 } 124 125 public Map<Command, Callback> getCallbackMap() { 126 return callbackMap; 127 } 128 129 public Queue<Command> getCommandQueue() { 130 return commandQueue; 131 } 132}