001package com.github.theholywaffle.teamspeak3; 002 003/* 004 * #%L 005 * TeamSpeak 3 Java API 006 * %% 007 * Copyright (C) 2014 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.api.reconnect.ConnectionHandler; 032import com.github.theholywaffle.teamspeak3.commands.CQuit; 033import com.github.theholywaffle.teamspeak3.commands.Command; 034import com.github.theholywaffle.teamspeak3.log.LogHandler; 035 036import java.util.concurrent.ExecutorService; 037import java.util.concurrent.Executors; 038import java.util.logging.Handler; 039import java.util.logging.Level; 040import java.util.logging.Logger; 041 042public class TS3Query { 043 044 public enum FloodRate { 045 DEFAULT(350), 046 UNLIMITED(0); 047 048 private final int ms; 049 050 FloodRate(int ms) { 051 this.ms = ms; 052 } 053 054 public int getMs() { 055 return ms; 056 } 057 } 058 059 public static final Logger log = Logger.getLogger(TS3Query.class.getName()); 060 061 private final ExecutorService userThreadPool = Executors.newCachedThreadPool(); 062 private final EventManager eventManager = new EventManager(); 063 private final TS3Config config; 064 private final ConnectionHandler connectionHandler; 065 066 private QueryIO io; 067 private TS3Api api; 068 private TS3ApiAsync asyncApi; 069 070 /** 071 * Creates a TS3Query that connects to a TS3 server at 072 * {@code localhost:10011} using default settings. 073 */ 074 public TS3Query() { 075 this(new TS3Config()); 076 } 077 078 /** 079 * Creates a customized TS3Query that connects to a server 080 * specified by {@code config}. 081 * 082 * @param config 083 * configuration for this TS3Query 084 */ 085 public TS3Query(TS3Config config) { 086 log.setUseParentHandlers(false); 087 log.addHandler(new LogHandler(config.getDebugToFile())); 088 log.setLevel(config.getDebugLevel()); 089 this.config = config; 090 this.connectionHandler = config.getReconnectStrategy().create(config.getConnectionHandler()); 091 } 092 093 // PUBLIC 094 095 public TS3Query connect() { 096 QueryIO oldIO = io; 097 if (oldIO != null) { 098 oldIO.disconnect(); 099 } 100 101 try { 102 io = new QueryIO(this, config); 103 } catch (TS3ConnectionFailedException conFailed) { 104 fireDisconnect(); 105 throw conFailed; 106 } 107 108 try { 109 connectionHandler.onConnect(this); 110 } catch (Throwable t) { 111 log.log(Level.SEVERE, "ConnectionHandler threw exception in connect handler", t); 112 } 113 io.continueFrom(oldIO); 114 115 return this; 116 } 117 118 /** 119 * Removes and closes all used resources to the teamspeak server. 120 */ 121 public void exit() { 122 // Send a quit command synchronously 123 // This will guarantee that all previously sent commands have been processed 124 doCommand(new CQuit()); 125 126 io.disconnect(); 127 userThreadPool.shutdown(); 128 for (final Handler lh : log.getHandlers()) { 129 log.removeHandler(lh); 130 } 131 } 132 133 public TS3Api getApi() { 134 if (api == null) { 135 api = new TS3Api(this); 136 } 137 return api; 138 } 139 140 public TS3ApiAsync getAsyncApi() { 141 if (asyncApi == null) { 142 asyncApi = new TS3ApiAsync(this); 143 } 144 return asyncApi; 145 } 146 147 // INTERNAL 148 149 boolean doCommand(Command c) { 150 final long end = System.currentTimeMillis() + config.getCommandTimeout(); 151 final Object signal = new Object(); 152 final Callback callback = new Callback() { 153 @Override 154 public void handle() { 155 synchronized (signal) { 156 signal.notifyAll(); 157 } 158 } 159 }; 160 161 io.queueCommand(c, callback); 162 163 boolean interrupted = false; 164 while (!c.isAnswered() && System.currentTimeMillis() < end) { 165 try { 166 synchronized (signal) { 167 signal.wait(end - System.currentTimeMillis()); 168 } 169 } catch (final InterruptedException e) { 170 interrupted = true; 171 } 172 } 173 if (interrupted) { 174 // Restore the interrupt 175 Thread.currentThread().interrupt(); 176 } 177 178 if (!c.isAnswered()) { 179 log.severe("Command " + c.getName() + " was not answered in time."); 180 return false; 181 } 182 183 return c.getError().isSuccessful(); 184 } 185 186 void doCommandAsync(Command c) { 187 doCommandAsync(c, null); 188 } 189 190 void doCommandAsync(Command c, Callback callback) { 191 io.queueCommand(c, callback); 192 } 193 194 void submitUserTask(Runnable task) { 195 userThreadPool.submit(task); 196 } 197 198 EventManager getEventManager() { 199 return eventManager; 200 } 201 202 void fireDisconnect() { 203 userThreadPool.submit(new Runnable() { 204 @Override 205 public void run() { 206 try { 207 connectionHandler.onDisconnect(TS3Query.this); 208 } catch (Throwable t) { 209 log.log(Level.SEVERE, "ConnectionHandler threw exception in disconnect handler", t); 210 } 211 } 212 }); 213 } 214}