Java tutorial
/* Copyright (C) 2001-2002 Taylor Gautier * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * $Id: AudioTron.java,v 1.1 2002/08/05 00:06:41 tgautier Exp $ */ package JavaTron; import java.net.*; import java.io.*; import javax.swing.text.*; import javax.swing.text.html.*; import java.lang.*; import java.lang.reflect.*; import java.util.*; import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.*; /** * Implements the AudioTron api. The AudioTron object calls all api command * requests on a seperate thread. All calls to the AudioTron object API's are * non blocking. * <p> * State is notified via a AudioTronListener object. If you want state use * an AudioTronState object instead. * * @author Taylor Gautier * @version $Revision: 1.1 $ */ public class AudioTron { // public constants public final Integer ON = new Integer(1); public final Integer OFF = new Integer(-1); public final Integer TOGGLE = new Integer(0); // private constants private final String USER_AGENT = "JavaTron/" + Version.version; public static final int NONE = 0; public static final int COMMAND = 1; public static final int INFO = 2; public static final int CLOCK = 3; public static final int SET_CLOCK = 4; public static final int GLOBAL = 5; public static final int TOC = 6; public static final int NAP = 7; public static final int DELQUE = 8; public static final int ADDNEW = 9; public static final int SEARCH = 10; public static final int MSG = 11; public static final int ALARM = 12; public static final int LAST_STATUS = ALARM; private int status = NONE; protected Hashtable statusMap = new Hashtable(); // command thread private CommandThread commandThread; // internal debugging constants protected boolean showAddress = true; protected boolean showUnparsedOutput = false; protected boolean showGet = false; protected boolean showPost = true; protected boolean showAuth = false; protected boolean showExtra = true; protected StringBuffer getBuffer; // the get connection, so it can be terminated InputStreamReader isr = null; /** * Describes an interface that an object needs to adhere to * for it to be a playable object by an AT */ public interface Playable { public String getType(); public String getFile(); //public String getSource(); public boolean isPlayable(); } public static interface Parser { /** * Called when the parsing sequence begins - error is null if * connection was made, !null if there is an error connecting * * @param error null if connection made, !null if not made, value * indicates the error */ public void begin(String error); /** * Called when the parsing sequence ends. If an error has occurred * the error flag is set * * @param error set if an error has occurred. */ public void end(boolean error); /** * /** * Called for each line to parse * * @return true if this method handled the parse * * @throws an exception if there is an error in parsing and * the data operation should be aborted */ public boolean parse(String content); } /** * A helper class that makes creating a parser class easy */ public abstract static class AbstractParser implements Parser { @Override public void begin(String error) { } @Override public void end(boolean error) { } @Override public boolean parse(String content) { return true; } } /** * Zero parameter version of constructor -- default settings for address, * username, and password. Sets up the thread and other initializations. */ public AudioTron() { this(null); } /** * One parameter version of constructor -- allows setting of AudioTron * address (default username and password) * * @param server the AudioTron ip address (defaults to 192.168.0.10) */ public AudioTron(String server) { this(server, null); } /** * Two parameter version of constructor -- allows setting of AudioTron * address and username (default password). * * @param server the AudioTron ip address (defaults to 192.168.0.10) * @param username the username to use when accessing AudioTron pages * (defaults to 'admin') */ public AudioTron(String server, String username) { this(server, username, null); } /** * Three parameter version of constructor -- allows setting of AudioTron * address, username, and password. * * @param server the AudioTron ip address (defaults to 192.168.0.10) * @param username the username to use when accessing AudioTron pages * (defaults to 'admin') * @param password the password to use when accessing the AudioTron pages * (defaults to 'admin') */ public AudioTron(String server, String username, String password) { setDefaults(); if (server != null) Configuration.setProperty(Configuration.KEY_SERVER, server); if (username != null) Configuration.setProperty(Configuration.KEY_USERNAME, username); if (password != null) Configuration.setProperty(Configuration.KEY_PASSWORD, password); // Setup the status mappings statusMap.put(new Integer(COMMAND), "Cmd"); statusMap.put(new Integer(INFO), "Info"); statusMap.put(new Integer(CLOCK), "Get Clock"); statusMap.put(new Integer(SET_CLOCK), "Set Clock"); statusMap.put(new Integer(GLOBAL), "Global"); statusMap.put(new Integer(TOC), "Save TOC"); statusMap.put(new Integer(NAP), "Set Nap"); statusMap.put(new Integer(ALARM), "Set Alarm"); statusMap.put(new Integer(DELQUE), "Removing"); statusMap.put(new Integer(ADDNEW), "Adding"); statusMap.put(new Integer(SEARCH), "Seaching"); statusMap.put(new Integer(MSG), "Display"); // Setup the HttpURLConnection class HttpURLConnection.setFollowRedirects(false); // Fire up the Command processor thread commandThread = new CommandThread(this); // Fire up the NTP daemon new MSNTPDaemon(); } public void setDefaults() { Properties defaults = new Properties(); String SP = System.getProperty("file.separator"); defaults.setProperty(Configuration.KEY_SERVER, "192.168.0.10"); defaults.setProperty(Configuration.KEY_USERNAME, "admin"); defaults.setProperty(Configuration.KEY_PASSWORD, JTP.encrypt("admin")); defaults.setProperty(Configuration.KEY_BASE_M3U_FILE, System.getProperty("user.home") + SP + "Desktop" + SP + "test.m3u"); defaults.setProperty(Configuration.KEY_DEFAULT_PLAYLIST_PATH, System.getProperty("user.home")); defaults.setProperty(Configuration.KEY_TV_BGCOLOR, "0,0,0"); defaults.setProperty(Configuration.KEY_TV_FONT, "Arial"); defaults.setProperty(Configuration.KEY_TV_FONT_COLOR, "100,100,100"); defaults.setProperty(Configuration.KEY_TV_FONT_SIZE, "20"); Configuration.setDefault(defaults); } public String getServer() { return Configuration.getProperty(Configuration.KEY_SERVER); } public String getUsername() { return Configuration.getProperty(Configuration.KEY_USERNAME); } public String getPassword() { return (new String(JTP.decrypt(Configuration.getProperty(Configuration.KEY_PASSWORD)))); } public String getBaseM3U() { return Configuration.getProperty(Configuration.KEY_BASE_M3U_FILE); } public String getPlaylistDefaultPath() { return Configuration.getProperty(Configuration.KEY_DEFAULT_PLAYLIST_PATH); } public void start() { } /** * Tell the AudioTron to Stop */ public void stop() { command("stop"); } /** * Tell the AudioTron to Play */ public void play() { command("play"); } /** * Tell the AudioTron to go back (Prev) */ public void prev() { command("prev"); } /** * Tell the AudioTron to go forward (Next) */ public void next() { command("next"); } /** * Tell the AudioTron to toggle the Pause setting */ public void pause() { pause(TOGGLE); } /** * Tell the AudioTron to Pause * * @param state the state to set the setting to. Must be one of * {@link #ON}, {@link #OFF}, {@link #TOGGLE}. */ public void pause(Integer state) { command("pause", state.toString()); } /** * Tell the AudioTron to clear the playlist */ public void clear() { clear(true); } /** * Tell the AudioTron to clear the playlist */ public void clear(boolean update) { command("clear"); } /** * Tell the AudioTron to toggle the Random setting */ public void random() { random(TOGGLE); } /** * Tell the AudioTron to set the Random setting * * @param state the state to set the setting to. Must be one of * {@link #ON}, {@link #OFF}, {@link #TOGGLE}. */ public void random(Integer state) { command("random", state.toString()); } /** * Tell the AudioTron to toggle the Repeat setting */ public void repeat() { repeat(TOGGLE); } /** * Tell the AudioTron to set the Repeat setting * * @param state the state to set the setting to. Must be one of * {@link #ON}, {@link #OFF}, {@link #TOGGLE}. */ public void repeat(Integer state) { command("repeat", state.toString()); } /** * Tell the AudioTron to toggle the Mute setting */ public void mute() { mute(TOGGLE); } /** * Tell the AudioTron to set the Mute setting * * @param state the state to set the setting to. Must be one of * {@link #ON}, {@link #OFF}, {@link #TOGGLE}. */ public void mute(Integer state) { command("mute", state.toString()); } /** * Tell the AudioTron to go to a new index * * @param index the index to go to */ public void gotoIndex(int index) { command("goto", new Integer(index).toString()); } /** * Tell the AudioTron to go to a different position within a song * * @param pos the new song position */ public void setSongPosition(int pos) { command("position", new Integer(pos).toString()); } /** * Tell the AudioTron to set the volume to the specified value * * @param volume the new volume */ public void setVolume(int volume) { command("volume", new Integer(volume).toString()); } public void setMsgOff() { command("msgoff"); } /** * Get the clock ctrl page * * @param parser the parser that will parse the result */ public void getClockCtrl(Parser parser) { GetCommand command = new GetCommand("/clockctrl.asp", null, parser, CLOCK); addCommand(command, 0); } /** * Enable nap timer */ public void setNap(int minutes, boolean softWake) { Vector commandArgs = new Vector(); commandArgs.add("naptime"); commandArgs.add(new Integer(minutes).toString()); commandArgs.add("softenable"); commandArgs.add(softWake ? "On" : "Off"); GetCommand command = new GetCommand("/goform/webSetNapForm", commandArgs, null, NAP); addCommand(command, 0); } public void setAlarm(int index, boolean enabled, boolean softEnabled, int hour, int min, boolean am, BitSet days, int volume) { if (index < 0 || index > 1) { return; } Vector commandArgs = new Vector(); commandArgs.add("alarmenable"); commandArgs.add(enabled ? "ON" : "OFF"); commandArgs.add("alarmindex"); commandArgs.add(new Integer(index).toString()); if (enabled) { commandArgs.add("softenable"); commandArgs.add(softEnabled ? "ON" : "OFF"); commandArgs.add("alarmhour"); commandArgs.add(new Integer(hour).toString()); commandArgs.add("alarmmin"); commandArgs.add(new Integer(min).toString()); commandArgs.add("ampmctrl"); commandArgs.add(am ? "1" : "2"); if (days.get(0)) { commandArgs.add("alarmmon"); commandArgs.add("ON"); } if (days.get(1)) { commandArgs.add("alarmtue"); commandArgs.add("ON"); } if (days.get(2)) { commandArgs.add("alarmwed"); commandArgs.add("ON"); } if (days.get(3)) { commandArgs.add("alarmthu"); commandArgs.add("ON"); } if (days.get(4)) { commandArgs.add("alarmfri"); commandArgs.add("ON"); } if (days.get(5)) { commandArgs.add("alarmsat"); commandArgs.add("ON"); } if (days.get(6)) { commandArgs.add("alarmsun"); commandArgs.add("ON"); } commandArgs.add("volumectrl"); commandArgs.add(new Integer(volume).toString()); } GetCommand command = new GetCommand("/goform/webSetAlarmForm", commandArgs, null, ALARM); addCommand(command, 0); } /** * Retrieve the global info page */ public void getGlobalInfo(Parser parser) { Vector commandArgs = new Vector(); commandArgs.add("type"); commandArgs.add("global"); GetCommand command = new GetCommand("/apigetinfo.asp", commandArgs, parser, GLOBAL); addCommand(command, 0); } /** * Retrieve a TOC file */ public void getTOC(String share, Parser parser) { Vector commandArgs = new Vector(); commandArgs.add("share"); commandArgs.add(share); GetCommand command = new GetCommand("/apidumptoc.asp", commandArgs, parser, TOC); addCommand(command, 0); } /** * Tell the AT to set it's clock from the given server * * @param timeServer the NTP time server to set the clock from * @param parser the parser to use to parse the result */ public void setTimeFromTimeServer(String timeServer, Parser parser) { Vector commandArgs = new Vector(); commandArgs.add("timeserver"); commandArgs.add(timeServer); commandArgs.add("settime button"); commandArgs.add("Set Time from Time Server"); GetCommand command = new GetCommand("/goform/webSetTimeNTPForm", commandArgs, parser, SET_CLOCK); addCommand(command, 0); } public void getFilteredInfo(String filter, String filterValue, Parser parser) { getFilteredInfo(filter, filterValue, null, parser); } public void getFilteredInfo(String filter, String filterValue, String start, Parser parser) { getFilteredInfo(filter, filterValue, start, 0, parser); } public void getFilteredInfo(String filter, String filterValue, int count, Parser parser) { getFilteredInfo(filter, filterValue, null, 0, parser); } public void getFilteredInfo(String filter, String filterValue, String start, int count, Parser parser) { String f = "f" + filter.toLowerCase(); getInfo("file", f, filterValue, start, count, parser); } /** * Tell the audiotron to retrieve info listing (query song database) */ public void getInfo(String type, Parser parser) { getInfo(type, null, null, null, 0, parser); } public void getInfo(String type, String start, Parser parser) { getInfo(type, start, 0, parser); } public void getInfo(String type, int count, Parser parser) { getInfo(type, null, count, parser); } public void getInfo(String type, String start, int count, Parser parser) { getInfo(type, null, null, start, count, parser); } private void getInfo(String type, String filter, String filterValue, String start, int count, Parser parser) { Vector commandArgs = new Vector(); commandArgs.add("type"); commandArgs.add(type); if (filter != null) { commandArgs.add(filter); commandArgs.add(filterValue); } if (start != null) { commandArgs.add("this"); commandArgs.add(start); } if (count > 0) { commandArgs.add("count"); commandArgs.add(new Integer(count).toString()); } GetCommand command = new GetCommand("/apigetinfo.asp", commandArgs, parser, INFO); addCommand(command, 1); } public void queueFile(Object playable) { try { queueFile((Playable) playable); } catch (ClassCastException cce) { // ignore } } public void queueFile(Object[] playable) { try { for (int i = 0; i < playable.length; i++) { queueFile((Playable) playable[i]); } } catch (ClassCastException cce) { // ignore } } /** * Tell the audiotron to queue a file * * type is the type of file to queue * file is the file to queue * * @param playable A Playable object type, the file to add to the queue */ public void queueFile(Playable playable) { if (!playable.isPlayable()) { return; } Vector commandArgs = new Vector(); commandArgs.add("type"); commandArgs.add(playable.getType()); commandArgs.add("file"); commandArgs.add(playable.getFile()); GetCommand command = new GetCommand("/apiqfile.asp", commandArgs, null); addCommand(command, 0); } public void playFile(Object playable) { try { playFile((Playable) playable); } catch (ClassCastException cce) { // ignore } } /** * Tell the audiotron to play a file (same as queueFile but clears * the playlist first) * * type is the type of file to queue * file is the file to queue * @param playable A Playable Object - a file, or list */ public void playFile(Playable playable) { if (!playable.isPlayable()) { return; } Vector list = new Vector(); list.addElement(playable); playFile(list.elements()); } /** * Method to remove a Song from the Audiotron's Active Play Queue * The only way to do this is through the web interface, with a POST * request. Probably should use a Vector for consistency, but I'm * still learning how all this put together. * * @author JSC * @param args A HashMap of the POST form data */ public void dequeueFiles(HashMap args) { Vector commandArgs = new Vector(); int i = 0; Set set = args.entrySet(); Iterator idx = set.iterator(); while (idx.hasNext()) { Map.Entry me = (Map.Entry) idx.next(); commandArgs.add(i, me.getKey()); commandArgs.add(i + 1, me.getValue()); i += 2; } PostCommand command = new PostCommand("/goform/webQuePlayForm", commandArgs, null, DELQUE); if (showExtra) { System.out.println("Dequeueing ...."); } addCommand(command, 0); } /** * Tell the audiotron to play a file (same as queueFile but clears * the playlist first) * * @param type an array of the type of file to queue * @param file and array of the file to queue */ public void playFile(Enumeration list) { boolean queued = false; while (list.hasMoreElements()) { Playable p = (Playable) list.nextElement(); if (p.isPlayable() && !queued) { queued = true; stop(); clear(false); } queueFile(p); } if (queued) { play(); } } /** * Method that tell the Audiotron to add a single file to its library -JSC * @param songLoc the SMB URL of the file to add */ public void addToLibrary(String songLoc) { Vector commandArgs = new Vector(); commandArgs.add("type"); commandArgs.add("file"); commandArgs.add("file"); commandArgs.add(songLoc); GetCommand command = new GetCommand("/apiaddfile.asp", commandArgs, null, ADDNEW); if (showExtra) { System.out.println("Adding " + songLoc); } //addCommand(command, 0); } /** * Method to add a new share * * @param shareURL */ public void add_new_share(String shareURL) { } /** * The POST request that tells the Audiotron to search for new files - JSC * TODO: need feedback...this is a LONG operation */ public void search() { Vector commandArgs = new Vector(); commandArgs.add("Hosts"); commandArgs.add("Press to Check"); PostCommand command = new PostCommand("/goform/CheckNewFilesForm", commandArgs, null, SEARCH); if (showExtra) { System.out.println("Searching for new Files."); } addCommand(command, 0); } /** * Tell's the audiotron to re-read the radio.txt file * */ public void checkRadioText() { Vector commandArgs = new Vector(); commandArgs.add("Search"); commandArgs.add("Search"); PostCommand command = new PostCommand("/goform/CheckNewStationsForm", commandArgs, null, SEARCH); if (showExtra) { System.out.println("Searching for new internet stations."); addCommand(command, 0); } } /** * POST's winXP/2000 network auth information to AT * @param username * @param password */ public void setXPAuth(String username, String password) { Vector commandArgs = new Vector(); commandArgs.add("ntuser"); commandArgs.add(username); commandArgs.add("ntpass"); commandArgs.add(password); commandArgs.add("NTSubmit"); commandArgs.add("Save NT/2000/XP Settings"); PostCommand command = new PostCommand("/goform/NTForm", commandArgs, null); addCommand(command, 0); } /** * POST's win98 share password to the AT * @param password */ public void setWin98Auth(String password) { Vector commandArgs = new Vector(); commandArgs.add("sharepass"); commandArgs.add(password); commandArgs.add("sharesubmit"); commandArgs.add("Save Share Password"); PostCommand command = new PostCommand("goform/ShareForm", commandArgs, null); addCommand(command, 0); } /** * Gets a web page from the Audiotron * * @param address - The URL to GET * @param commandArgs - Arguments for a GET request * @param parser - An HtmlParser to parse/return the output */ public void getPage(String address, Vector commandArgs, JTP.HtmlParser parser) { GetCommand command = new GetCommand(address, commandArgs, parser, INFO); if (showExtra) { System.out.println("Getting " + address); } addCommand(command, 0); } /** * Issue a command * * @param command the command to execute */ private void command(String command) { command(command, null); } /** * Issue a command. This method queues the command onto the priority * queue. It is non-blocking. * * @param command the command to execute * @param state additional args */ private void command(String cmd, String state) { Vector commandArgs = new Vector(); commandArgs.add("cmd"); commandArgs.add(cmd); if (state != null) { commandArgs.add("arg"); commandArgs.add(state); } GetCommand command = new GetCommand("/apicmd.asp", commandArgs, null); addCommand(command, 0); } /** * A Command that does a get */ protected class GetCommand extends Command { String address; Vector args; Parser parser; /** * Get command constructor * * @param address_ the (web page) address of the command * @param args_ args to the command * @param parser the parser to parse the result */ public GetCommand(String address_, Vector args_, Parser parser_) { this(address_, args_, parser_, COMMAND); } /** * Get command constructor * * @param address_ the (web page) address of the command * @param args_ args to the command * @param parser_ the parser to parse the result * @param status what status this command represents */ public GetCommand(String address_, Vector args_, Parser parser_, int status) { super(status); address = address_; args = args_; parser = parser_; } @Override public void invoke() { try { get(address, args, parser); } catch (IOException ioe) { // XXX what do here? } } } /** * A command that does a POST * */ protected class PostCommand extends Command { String address; Vector args; Parser parser; /** * Post command constructor * * @param address the (web page) address of the command * @param args args to the command * @param parser the parser to parse the result */ public PostCommand(String address_, Vector args_, Parser parser_) { this(address_, args_, parser_, COMMAND); } /** * Post command constructor * * @param address_ the (web page) address of the command * @param args_ args to the command * @param parser_ the parser to parse the result * @param status what status this command represents */ public PostCommand(String address_, Vector args_, Parser parser_, int status) { super(status); address = address_; args = args_; parser = parser_; } @Override public void invoke() { try { post(address, args, parser); } catch (IOException ioe) { // XXX what do here? } } } /** * Create URI arguments * * @param buffer the buffer to append the output string to * @param args the input arguments */ private void createURIArgs(StringBuffer buffer, Vector args) throws UnsupportedEncodingException { for (int i = 0; i < args.size(); i += 2) { if (i > 0) { buffer.append("&"); } buffer.append(URLEncoder.encode(args.elementAt(i).toString(), "UTF-8")); buffer.append("="); buffer.append(URLEncoder.encode(args.elementAt(i + 1).toString(), "UTF-8")); } } /** * Send a get request to the AudioTron server * * @param address the uri to use * @param args the arguments to use * @param parser an optional line-by-line parser to use instead of buffering * the whole response * * @return null on success, a string on error. The string describes * the error. * * @throws IOException if the process was interrupted */ protected String get(String address, Vector args, Parser parser) throws IOException { String ret = null; StringBuffer myAddress = new StringBuffer(); Object[] methodArgs = new Object[1]; try { // construct the address myAddress.append(address); if (args != null) { myAddress.append('?'); createURIArgs(myAddress, args); } if (showAddress) { System.out.println(myAddress.toString()); } HttpURLConnection conn = getConnection(myAddress.toString()); if (conn.getResponseCode() != 200 && conn.getResponseCode() != 302) { try { ret = conn.getResponseMessage(); } catch (IOException ioe) { ioe.printStackTrace(); } if (ret == null) { ret = "Unknown Error"; } if (parser != null) { parser.begin(ret); } return ret; } isr = new InputStreamReader(conn.getInputStream()); BufferedReader reader = new BufferedReader(isr); String s; getBuffer = new StringBuffer(); if (parser != null) { parser.begin(null); } while ((s = reader.readLine()) != null) { if (showGet) { System.out.println(s); } getBuffer.append(s); getBuffer.append("\n"); if (parser == null) { // getBuffer.append(s); } else { if (!parser.parse(s)) { return "Parse Error"; } } } if (parser == null && showUnparsedOutput) { System.out.println(getBuffer); } if (parser != null) { parser.end(false); } } catch (MalformedURLException mue) { mue.printStackTrace(); } catch (IOException ioe) { // if this happens, call the parse method if there is a parser // with a null value to indicate an error if (parser != null) { parser.end(true); } throw (ioe); } finally { try { isr.close(); } catch (Exception e) { // this is ok } } //return getBuffer.toString(); return ret; } /** * Method to execute a POST Request to the Audiotron - JSC * * @param address - The requested page * @param args - The POST data * @param parser - A Parser Object fit for parsing the response * * @return null on success, a string on error. The string describes * the error. * * @throws IOException */ protected String post(String address, Vector args, Parser parser) throws IOException { String ret = null; URL url; HttpURLConnection conn; String formData = new String(); // Build the POST data for (int i = 0; i < args.size(); i += 2) { if (showPost) { System.out.print("POST: " + args.get(i).toString() + " = "); System.out.println(args.get(i + 1).toString()); } formData += URLEncoder.encode(args.get(i).toString(), "UTF-8") + "=" + URLEncoder.encode(args.get(i + 1).toString(), "UTF-8"); if (i + 2 != args.size()) formData += "&"; } // Build the connection Headers and POST the data try { url = new URL("http://" + getServer() + address); if (showAddress || showPost) { System.out.println("POST: " + address); System.out.println("POST: " + formData); } conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("POST"); conn.setRequestProperty("User-Agent", USER_AGENT); // Authorization header String auth = getUsername() + ":" + getPassword(); conn.setRequestProperty("Authorization", "Basic " + B64Encode(auth.getBytes())); // Debug if (showAuth) { System.out.println("POST: AUTH: " + auth); } conn.setRequestProperty("Content Type", "application/x-www-form-urlencoded"); conn.setRequestProperty("Content-Length", "" + Integer.toString(formData.getBytes().length)); conn.setRequestProperty("Content-Language", "en-US"); conn.setUseCaches(false); conn.setDoInput(true); conn.setDoOutput(true); // Send the request to the audiotron DataOutputStream wr = new DataOutputStream(conn.getOutputStream()); wr.writeBytes(formData); wr.flush(); wr.close(); // Process the Response if (conn.getResponseCode() != 200 && conn.getResponseCode() != 302) { try { ret = conn.getResponseMessage(); } catch (IOException ioe) { ioe.printStackTrace(); } if (ret == null) { ret = "Unknown Error"; } if (parser != null) { parser.begin(ret); } return ret; } isr = new InputStreamReader(conn.getInputStream()); BufferedReader reader = new BufferedReader(isr); String s; getBuffer = new StringBuffer(); if (parser != null) { parser.begin(null); } while ((s = reader.readLine()) != null) { if (showGet) { System.out.println(s); } getBuffer.append(s); getBuffer.append("\n"); if (parser == null) { // getBuffer.append(s); } else { if (!parser.parse(s)) { return "Parse Error"; } } } if (parser == null && showUnparsedOutput) { System.out.println(getBuffer); } if (parser != null) { parser.end(false); } } catch (MalformedURLException mue) { mue.printStackTrace(); } catch (IOException ioe) { // if this happens, call the parse method if there is a parser // with a null value to indicate an error if (parser != null) { parser.end(true); } throw (ioe); } finally { try { isr.close(); } catch (Exception e) { // this is ok } } return ret; } /** * Initiate a HttpURLConnection to the AudioTron. Includes * setting the appropriate headers such as authentication. * * @param address the full http address to connect to * * @return an HttpURLConnection that is not connected * * @throws MalformedURLException if the address parameter is bad * @throws IOException if there is a problem opening the connection */ private HttpURLConnection getConnection(String address) throws MalformedURLException, IOException { // create the http url StringBuffer urlBuffer = new StringBuffer(); urlBuffer.append("http://"); urlBuffer.append(getServer()); urlBuffer.append(address); // setup the url and open a connection to it URL url = new URL(urlBuffer.toString()); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestProperty("User-Agent", USER_AGENT); // Authorization header String auth = getUsername() + ":" + getPassword(); conn.setRequestProperty("Authorization", "Basic " + B64Encode(auth.getBytes())); // Debug if (showAuth) { System.out.println("AUTH: " + auth); } return conn; } /** * BASE64 Encode a byte array * Function to replace sun.misc.BASE64 stuff * uses jakarta commons codec * @param s - the input to be encoded * @return a Base64 Encoded String fit for web work */ protected String B64Encode(byte[] s) { byte[] t = Base64.encodeBase64(s); String h = ""; for (int i = 0; i < t.length; i++) { h += (char) t[i]; } // Debug if (showExtra) { System.out.println("Encode: " + h); } return (h); } /** * Add a command to execute. Does so non-blocking. Just calls * the CommandThreads' addCommand method. * * @param command the callback method to invoke. * @param priority the priority of this command */ protected void addCommand(Command command, int priority) { commandThread.addCommand(command, priority); } /** * Remove a command from the execution queue. * * @param command the callback method to remove */ protected void removeCommand(Command command) { commandThread.removeCommand(command); } /** * Get the status of the currently executing command */ protected int getCommandStatus() { return commandThread.getCommandStatus(); } /** * Return a text description of the status */ public String getStatus() { Object obj = statusMap.get(new Integer(getCommandStatus())); if (obj != null) { return obj.toString(); } return ""; } /** * Call at the beginning a command */ protected synchronized void startCommand(Command command) { } /** * Call at the end of a a command. */ protected synchronized void endCommand(Command command) { } /** * Implements the command processing thread. This thread dequeues * items from the priority queue (which are assumed to be of type * Command) and invokes them. * */ private class CommandThread implements Runnable, PriorityQueue.ElementInsertionListener { private PriorityQueue queue; private AudioTron at; private PriorityQueue.PrioritizedElement current; public CommandThread(AudioTron at_) { at = at_; queue = new PriorityQueue(3); queue.addElementInsertionListener(this); Thread t = new Thread(this); t.setDaemon(true); t.start(); } /** * ElementInsertionListener implementation */ @Override public void elementInserted(Object element, int priority) { if (current != null && current.priority > priority) { try { isr.close(); } catch (Exception e) { // ignore any exceptions } } } /** * Add a command to execute. Does so non-blocking. * * @param wm the callback method to invoke. * @param the priority of this command */ public synchronized void addCommand(Command command, int priority) { queue.add(command, priority); notify(); } /** * Remove a command from the execution queue. * * @param wm the callback method to remove */ public void removeCommand(Command command) { queue.remove(command); } /** * Return the status value of the currently executing command */ public int getCommandStatus() { try { return ((Command) current.element).status; } catch (NullPointerException npe) { // better than synchronizing } return NONE; } /** * The run method which is the start point of the thread. */ @Override public void run() { Command command = null; for (;;) { synchronized (this) { while (queue.isEmpty()) { try { wait(); } catch (InterruptedException ie) { // do nothing } } current = (PriorityQueue.PrioritizedElement) queue.remove(); } try { command = (Command) current.element; startCommand(command); command.invoke(); } catch (Exception e) { e.printStackTrace(); } finally { current = null; if (command != null) { endCommand(command); } command = null; } } } } /** * Command object is the basis for executing "commands" from an * ordered, priority queue on a seperate "command thread" for * asynchronous behavior. See Design Patterns (Command Pattern). */ public abstract static class Command { int status; public Command(int status_) { status = status_; } public abstract void invoke() throws Exception; } }