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}