/*
* XCTrack - XContest Live Tracking client for J2ME devices
* Copyright (C) 2009 Petr Chromec <petr@xcontest.org>
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
package org.xcontest.live;
import java.util.Vector;
import org.xcontest.live.message.RequestOpen;
import org.xcontest.live.message.RequestOpenAnonymous;
import org.xcontest.live.message.RequestOpenContinue;
import org.xcontest.live.message.RequestOpenRegistered;
import org.xcontest.live.message.RequestTrackPart;
import org.xcontest.live.message.ResponseOpenLoginFailed;
import org.xcontest.live.message.ResponseOpenOk;
import org.xcontest.live.message.ResponseTrackPart;
import org.xcontest.live.message.ServerMessage;
public class LiveClient {
public final static int DEFAULT_RESEND_INTERVAL = 60*1000;
public final static int DEFAULT_RECEIVE_RECONNECT_INTERVAL = 30*1000;
public final static double DEFAULT_MESSAGE_INTERVAL = 60;
/**
* Neither reader nor writer thread is running, session is closed
*/
public static final int STATE_CLOSED = 1;
/**
* Both threads are running, not yet received ResponseOpenOk
*/
public static final int STATE_OPENING = 2;
/**
* Both threads are running, session is opened (received ResponseOpenOk)
*/
public static final int STATE_OPEN = 3;
/**
* Both threads are running, trying to send out remaining messages
*/
public static final int STATE_CLOSING = 4;
public LiveClient() {
_conn = null;
_listeners = new Vector();
_pointBuffer = new Vector();
_loggers = new Vector();
_state = STATE_CLOSED;
_trackType = TrackType.DEFAULT.getValue();
_competitionKey = null;
_isPublic = true;
_messageInterval = (long)(1000*DEFAULT_MESSAGE_INTERVAL);
_nextMessageTime = -1;
}
/**
*
* @param npoints number of tracklog points to form one message
*/
public synchronized void setMessageInterval(double val) {
_messageInterval = (long)(val*1000);
_nextMessageTime = -1;
}
public synchronized void setConnection(LiveConnection conn) {
if (_state != STATE_CLOSED)
throw new ArgumentError("Cannot setConnection() - tracking already started");
_conn = conn;
_conn.setClient(this);
}
public void addListener(LiveClientListener l) {
synchronized(this) {
_listeners.addElement(l);
}
}
public void removeListener(LiveClientListener l) {
synchronized(this) {
_listeners.removeElement(l);
}
}
public void addLogger(Logger l) {
synchronized(_loggers) {
_loggers.addElement(l);
}
}
public void logInfo(String msg) {
synchronized(_loggers) {
for (int i = 0; i < _loggers.size(); i ++)
((Logger)_loggers.elementAt(i)).info(msg);
}
}
public void logError(String msg) {
synchronized(_loggers) {
for (int i = 0; i < _loggers.size(); i ++)
((Logger)_loggers.elementAt(i)).error(msg);
}
}
public void logError(String msg,Throwable e) {
synchronized(_loggers) {
for (int i = 0; i < _loggers.size(); i ++)
((Logger)_loggers.elementAt(i)).error(msg,e);
}
}
public synchronized void setTrackType(String trackType) {
_trackType = trackType;
}
public synchronized void setCompetitionKey(String competitionKey) {
_competitionKey = competitionKey;
}
public synchronized void setPublic(boolean isPublic) {
_isPublic = isPublic;
}
private synchronized void startTracking() {
if (_conn == null) {
throw new Error("Cannot start tracking - set LiveConnection first!");
}
if (_state != STATE_CLOSED)
closeSession();
_state = STATE_OPENING;
_pointBuffer.removeAllElements();
_sender = new LiveSender(this,_conn,_openRequest);
_sender.start();
_receiver = new LiveReceiver(this,_conn);
_receiver.start();
}
public synchronized void openRegisteredSession(String domain, String username, String password) {
_openRequest = new RequestOpenRegistered(domain,username,password,_trackType,_competitionKey,_isPublic);
startTracking();
}
public synchronized void openAnonymousSession(String firstname, String surname, String nickname) {
_openRequest = new RequestOpenAnonymous(firstname,surname,nickname,_trackType,_competitionKey,_isPublic);
startTracking();
}
public synchronized void continueSession(String key) {
_key = key;
_openRequest = new RequestOpenContinue(key);
startTracking();
}
public synchronized void closeSession() {
if (_state != STATE_CLOSED) {
_state = STATE_CLOSED;
_sender.stop();
_receiver.stop();
_conn.close();
_nextMessageTime = -1;
}
}
public synchronized void addPoint(GpsPoint p) {
_pointBuffer.addElement(p);
if (_nextMessageTime < 0) {
_nextMessageTime = System.currentTimeMillis() + _messageInterval;
}
}
public synchronized void checkSendPositionMessage() {
int n = _pointBuffer.size();
if (_sender != null && n > 0) {
long now = System.currentTimeMillis();
if (_nextMessageTime <= now) { // time for next message
RequestTrackPart msg = new RequestTrackPart(_segmentSeq,_pointBuffer);
_pointBuffer.removeAllElements();
_segmentSeq ++;
_sender.addTrackMessage(msg);
_nextMessageTime += _messageInterval;
if (_nextMessageTime < now)
_nextMessageTime = now;
for (int i = 0; i < _listeners.size(); i ++)
((LiveClientListener)_listeners.elementAt(i)).statusUpdate();
}
}
}
public synchronized int getConfirmedMessages() {
if (_sender != null)
return _sender.getOKCounter();
else
return 0;
}
public synchronized int getPendingMessages() {
if (_sender != null)
return _sender.getPendingCounter();
else
return 0;
}
public synchronized int getConfirmedPoints() {
if (_sender != null)
return _sender.getConfirmedPoints();
else
return 0;
}
public synchronized int getPendingPoints() {
if (_sender != null)
return _sender.getPendingPoints() + _pointBuffer.size();
else
return _pointBuffer.size();
}
// Notification from RECEIVER thread
synchronized void receivedMessage(ServerMessage msg) {
if (msg instanceof ResponseOpenOk) {
ResponseOpenOk resp = (ResponseOpenOk)msg;
boolean doNotify = false;
String recvDomain = null;
String recvUsername = null;
String key = null;
if (_state == STATE_OPENING) {
recvDomain = resp.getDomain();
recvUsername = resp.getUsername();
doNotify = true;
if (resp.getKey() != null)
_key = key = resp.getKey();
else
key = _key;
_segmentSeq = resp.getSeqCount();
_state = STATE_OPEN;
}
if (doNotify) {
_sender.receivedOpenResponseOk(resp);
synchronized(this) {
for (int i = 0; i < _listeners.size(); i ++)
((LiveClientListener)_listeners.elementAt(i)).loginOK(key, recvDomain, recvUsername);
}
}
}
else if (msg instanceof ResponseOpenLoginFailed) {
ResponseOpenLoginFailed resp = (ResponseOpenLoginFailed)msg;
// notify listeners
synchronized(this) {
for (int i = 0; i < _listeners.size(); i ++)
((LiveClientListener)_listeners.elementAt(i)).loginFailed(resp.getDomain(),resp.getUsername());
}
/* this is done from listeners through closeSession()
_state = STATE_CLOSED;
_conn.close();
_sender.stop();
_receiver.stop(); // interrupt meeee
*/
}
else if (msg instanceof ResponseTrackPart) {
ResponseTrackPart track = (ResponseTrackPart)msg;
_sender.receivedTrackPartResponse(track);
}
synchronized(this) {
for (int i = 0; i < _listeners.size(); i ++)
((LiveClientListener)_listeners.elementAt(i)).statusUpdate();
}
}
// Notification from RECEIVER thread
void connectionOpened(String remoteEnd) {
synchronized(this) {
for (int i = 0; i < _listeners.size(); i ++)
((LiveClientListener)_listeners.elementAt(i)).connectionOpened(remoteEnd);
}
}
// Notification from RECEIVER thread
void connectionOpenFailed(String remoteEnd) {
synchronized(this) {
for (int i = 0; i < _listeners.size(); i ++)
((LiveClientListener)_listeners.elementAt(i)).openConnectionFailed(remoteEnd);
}
}
// Notification from SENDER thread
void messageSent() {
synchronized(this) {
for (int i = 0; i < _listeners.size(); i ++)
((LiveClientListener)_listeners.elementAt(i)).statusUpdate();
}
}
private Vector _loggers; // LOCK
private LiveConnection _conn;
private Vector _listeners; // LOCK this < _listeners
private LiveSender _sender;
private LiveReceiver _receiver;
private RequestOpen _openRequest;
private int _state;
private Vector _pointBuffer;
private int _segmentSeq;
private String _key;
private String _trackType;
private String _competitionKey;
private boolean _isPublic;
private long _messageInterval;
private long _nextMessageTime;
}