
eu.hgross.blaubot.geobeacon.GeoLocationBeacon Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of blaubot Show documentation
Show all versions of blaubot Show documentation
An easy to use publish/subscribe middleware to create and communicate through dynamically created adhoc networks.
package eu.hgross.blaubot.geobeacon;
import com.google.gson.Gson;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import eu.hgross.blaubot.core.BeaconHelper;
import eu.hgross.blaubot.core.Blaubot;
import eu.hgross.blaubot.core.BlaubotConstants;
import eu.hgross.blaubot.core.BlaubotDevice;
import eu.hgross.blaubot.core.IBlaubotAdapter;
import eu.hgross.blaubot.core.IBlaubotConnection;
import eu.hgross.blaubot.core.State;
import eu.hgross.blaubot.core.acceptor.ConnectionMetaDataDTO;
import eu.hgross.blaubot.core.acceptor.IBlaubotIncomingConnectionListener;
import eu.hgross.blaubot.core.acceptor.IBlaubotListeningStateListener;
import eu.hgross.blaubot.core.acceptor.discovery.BeaconMessage;
import eu.hgross.blaubot.core.acceptor.discovery.IBlaubotBeacon;
import eu.hgross.blaubot.core.acceptor.discovery.IBlaubotBeaconStore;
import eu.hgross.blaubot.core.acceptor.discovery.IBlaubotDiscoveryEventListener;
import eu.hgross.blaubot.core.connector.IBlaubotConnector;
import eu.hgross.blaubot.core.statemachine.states.IBlaubotState;
import eu.hgross.blaubot.messaging.BlaubotMessage;
import eu.hgross.blaubot.messaging.BlaubotMessageReceiver;
import eu.hgross.blaubot.messaging.BlaubotMessageSender;
import eu.hgross.blaubot.messaging.IBlaubotMessageListener;
import eu.hgross.blaubot.util.Log;
/**
* A Beacon that commits its current state to a server flavoured with geolocation data
* to get updates about nearby devices that do the same.
*
* Does not commit updates, if in passive mode.
* GeoLocation data has to be commited to this beacon instance by calling setGeoData(...).
*
* This beacon class should be subclassed to integrate the platform specific geo location
* provider into the subclass' internals. Call setGeoData() to provide this data.
*/
public abstract class GeoLocationBeacon implements IBlaubotBeacon {
private static final String LOG_TAG = "GeoLocationBeacon";
private boolean discoveryActive;
private Gson gson;
private Blaubot blaubot;
private IBlaubotBeaconStore beaconStore;
private IBlaubotListeningStateListener listeningStateListener;
private IBlaubotIncomingConnectionListener acceptorListener;
private IBlaubotDiscoveryEventListener discoveryEventListener;
private final List connectors;
/**
* The last known csm state to adjust some timings for republishing.
*/
private State currentState;
/**
* Timestamp of the last beacon message publishing
*/
private long lastGeoBeaconMessagePublish;
/**
* receiver of the current connection to the beacon server
*/
private BlaubotMessageReceiver currentGeoBeaconConnectionMessageReceiver;
/**
* sender of the current connection to the beacon server
*/
private BlaubotMessageSender currentGeoBeaconConnectionMessageSender;
/**
* The current beacon message containing the state of the csm
*/
private BeaconMessage currentBeaconMessage;
/**
* The last known geo data for this device.
* Will be published to the GeoBeaconServer
*/
private GeoData currentGeoData;
/**
* The executor service used to connect to the GeoBeaconServer
*/
private ScheduledExecutorService beaconServerConnectExecutor;
private static final long CONNECT_PERIOD = 3000;
private Runnable connectTask = new Runnable() {
@Override
public synchronized void run() {
if (currentGeoBeaconConnectionMessageSender != null) {
return; // do nothing, if connceted
}
// connect
for (IBlaubotConnector connector : connectors) {
final IBlaubotConnection geoBeaconServerConnection = connector.connectToBlaubotDevice(new BlaubotDevice(GeoBeaconConstants.GEO_BEACON_SERVER_UNIQUE_DEVICE_ID));
if (geoBeaconServerConnection != null) {
currentGeoBeaconConnectionMessageSender = new BlaubotMessageSender(geoBeaconServerConnection);
currentGeoBeaconConnectionMessageReceiver = new BlaubotMessageReceiver(geoBeaconServerConnection);
currentGeoBeaconConnectionMessageReceiver.addMessageListener(messageListener);
currentGeoBeaconConnectionMessageSender.activate();
currentGeoBeaconConnectionMessageReceiver.activate();
try {
publishGeoBeaconMessageToServer();
} catch (Exception e) {
e.printStackTrace();
}
break;
}
}
}
};
/**
* We need to periodically re-publish the message to the server because there is a fixed max age
* for the records until they get erased. This executor will re-publish if needed
*/
private ScheduledExecutorService republishExecutor;
private static final long REPUBLISH_TASK_PERIOD = 3000;
private Runnable republishTask = new Runnable() {
/**
* Checks whether we need to update the state at the server because the last republish is too old
* @return true, iff we should update
*/
private boolean doRepublish() {
final long now = System.currentTimeMillis();
// we either use the max age (very slow re-publishs), if we are in a network
// or we send pretty fast, if we are eager to find partners
final double MAX_PERIOD_SINCE_LAST_PUBLISH = currentState != null && currentState == State.Free ? REPUBLISH_TASK_PERIOD : GeoBeaconConstants.MAX_AGE_BEACON_MESSAGES * 0.75;
return (now - lastGeoBeaconMessagePublish) > MAX_PERIOD_SINCE_LAST_PUBLISH;
}
@Override
public void run() {
if (!discoveryActive) {
return;
}
if (doRepublish()) {
publishGeoBeaconMessageToServer();
}
}
};
/**
* Gets notified by the beacon server if new interesting beacons are nearby
*/
private IBlaubotMessageListener messageListener = new IBlaubotMessageListener() {
@Override
public void onMessage(BlaubotMessage blaubotMessage) {
final GeoBeaconMessage geoBeaconMessage = GeoBeaconUtil.blaubotMessageToGeoBeaconMessage(blaubotMessage);
if (Log.logDebugMessages()) {
Log.d(LOG_TAG, "Got message from GeoBeaconServer: " + geoBeaconMessage);
}
if (discoveryEventListener != null) {
// create discovery event(s) and notify
final BeaconMessage beaconMessage = geoBeaconMessage.getBeaconMessage();
BeaconHelper.populateEventsFromBeaconMessage(beaconMessage, discoveryEventListener);
}
}
};
private UUID beaconUUID;
/**
* @param beaconStore The beacon store holding the connection meta data for the given connectors to connect to the GeoBeaconServer's acceptors.
* @param connectors connectors to be used to establish a connection to the beacon server
*/
public GeoLocationBeacon(IBlaubotBeaconStore beaconStore, IBlaubotConnector... connectors) {
this.connectors = Arrays.asList(connectors);
for (IBlaubotConnector connector : connectors) {
connector.setBeaconStore(beaconStore);
}
this.gson = new Gson();
}
@Override
public void setBlaubot(Blaubot blaubot) {
this.blaubot = blaubot;
this.beaconUUID = blaubot.getUuidSet().getBeaconUUID();
}
@Override
public void setBeaconStore(IBlaubotBeaconStore beaconStore) {
this.beaconStore = beaconStore;
}
@Override
public IBlaubotAdapter getAdapter() {
return null;
}
@Override
public synchronized void startListening() {
if (Log.logDebugMessages()) {
Log.d(LOG_TAG, "Starting GeoLocationBeacon ...");
}
if (beaconServerConnectExecutor != null) {
// already started
return;
}
// start connect thread
this.beaconServerConnectExecutor = Executors.newSingleThreadScheduledExecutor();
this.beaconServerConnectExecutor.scheduleWithFixedDelay(connectTask, 0, CONNECT_PERIOD, TimeUnit.MILLISECONDS);
// start re-publish thread
this.republishExecutor = Executors.newSingleThreadScheduledExecutor();
this.republishExecutor.scheduleWithFixedDelay(republishTask, 0, REPUBLISH_TASK_PERIOD, TimeUnit.MILLISECONDS);
// notify started
if (this.listeningStateListener != null) {
this.listeningStateListener.onListeningStarted(this);
}
if (Log.logDebugMessages()) {
Log.d(LOG_TAG, "GeoLocationBeacon started.");
}
}
@Override
public synchronized void stopListening() {
if (Log.logDebugMessages()) {
Log.d(LOG_TAG, "Stopping GeoLocationBeacon ...");
}
// stop connect thread
if (beaconServerConnectExecutor != null) {
beaconServerConnectExecutor.shutdownNow();
try {
beaconServerConnectExecutor.awaitTermination(CONNECT_PERIOD * 10, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
} finally {
beaconServerConnectExecutor = null;
}
}
if (republishExecutor != null) {
republishExecutor.shutdownNow();
try {
republishExecutor.awaitTermination(REPUBLISH_TASK_PERIOD * 10, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
} finally {
republishExecutor = null;
}
}
// disconnect connection and deactivate sender/receiver
if (this.currentGeoBeaconConnectionMessageReceiver != null) {
currentGeoBeaconConnectionMessageReceiver.getBlaubotConnection().disconnect();
this.currentGeoBeaconConnectionMessageReceiver.deactivate(null);
this.currentGeoBeaconConnectionMessageSender.deactivate(null);
this.currentGeoBeaconConnectionMessageReceiver.removeMessageListener(messageListener);
this.currentGeoBeaconConnectionMessageReceiver = null;
this.currentGeoBeaconConnectionMessageSender = null;
}
// notify
if (this.listeningStateListener != null) {
this.listeningStateListener.onListeningStopped(this);
}
if (Log.logDebugMessages()) {
Log.d(LOG_TAG, "GeoLocationBeacon stopped.");
}
}
@Override
public synchronized boolean isStarted() {
return beaconServerConnectExecutor != null;
}
@Override
public void setListeningStateListener(IBlaubotListeningStateListener stateListener) {
this.listeningStateListener = stateListener;
}
@Override
public void setAcceptorListener(IBlaubotIncomingConnectionListener acceptorListener) {
this.acceptorListener = acceptorListener;
}
@Override
public ConnectionMetaDataDTO getConnectionMetaData() {
// TODO separate the acceptor interface from the beacon interface
return new ConnectionMetaDataDTO();
}
@Override
public void setDiscoveryEventListener(IBlaubotDiscoveryEventListener discoveryEventListener) {
this.discoveryEventListener = discoveryEventListener;
}
@Override
public void onConnectionStateMachineStateChanged(IBlaubotState state) {
this.currentState = State.getStateByStatemachineClass(state.getClass());
this.currentBeaconMessage = blaubot.getConnectionStateMachine().getBeaconService().getCurrentBeaconMessage();
publishGeoBeaconMessageToServer();
}
@Override
public void setDiscoveryActivated(boolean active) {
discoveryActive = active;
}
/**
* Sets the current geo data
*
* @param geoData the best known location of this device
*/
public void setGeoData(GeoData geoData) {
this.currentGeoData = geoData;
publishGeoBeaconMessageToServer();
}
/**
* Builds and publishes the current GeoBeaconMessage to the server
*/
private void publishGeoBeaconMessageToServer() {
lastGeoBeaconMessagePublish = 0; // mark that we need to re-publish
if (currentGeoBeaconConnectionMessageSender == null || currentBeaconMessage == null || beaconUUID == null) {
return;
}
if (Log.logDebugMessages()) {
Log.d(LOG_TAG, "Publishing state to beacon server ...");
}
GeoBeaconMessage geoBeaconMessage = new GeoBeaconMessage(currentBeaconMessage, currentGeoData, beaconUUID.toString());
GeoBeaconMessageDTO dto = new GeoBeaconMessageDTO(geoBeaconMessage);
byte[] geoMessageDtoBytes = gson.toJson(dto).getBytes(BlaubotConstants.STRING_CHARSET);
BlaubotMessage msg = new BlaubotMessage();
msg.setPayload(geoMessageDtoBytes);
this.currentGeoBeaconConnectionMessageSender.sendMessage(msg);
this.lastGeoBeaconMessagePublish = System.currentTimeMillis();
if (Log.logDebugMessages()) {
Log.d(LOG_TAG, "Published state to beacon server.");
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy