com.openfin.desktop.channel.Channel Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of openfin-desktop-java-adapter Show documentation
Show all versions of openfin-desktop-java-adapter Show documentation
The Java API for OpenFin Runtime
package com.openfin.desktop.channel;
import java.util.Enumeration;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import com.openfin.desktop.*;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.accessibility.AccessibleKeyBinding;
/**
* The Channel object allows an OpenFin application to create a channel as a ChannelProvider, or connect to a channel as a ChannelClient.
* @author Anthony
*
*/
public class Channel {
private final static Logger logger = LoggerFactory.getLogger(Channel.class.getName());
private DesktopConnection desktopConnection;
private String name;
private ChannelProvider provider;
private ConcurrentHashMap clientMap;
private CopyOnWriteArrayList channelListeners;
private static String CONNECTED_EVENT = "connected";
public Channel(String name, DesktopConnection desktopConnection) {
this.name = name;
this.desktopConnection = desktopConnection;
this.clientMap = new ConcurrentHashMap<>();
this.channelListeners = new CopyOnWriteArrayList<>();
}
/**
* Get the name of the channel
* @return name of the channel
*/
public String getName() {
return this.name;
}
public DesktopConnection getDesktopConnection() {
return this.desktopConnection;
}
/**
* Create a new channel.
* @param callback The callback that receives the wrapped {@link ChannelProvider} object
*/
public void create(AsyncCallback callback) {
JSONObject payload = new JSONObject();
payload.put("channelName", this.name);
desktopConnection.sendAction("create-channel", payload, new AckListener() {
@Override
public void onSuccess(Ack ack) {
JSONObject providerIdentity = (JSONObject) ack.getData();
EndpointIdentity providerEndpointIdentity = new EndpointIdentity(providerIdentity);
ChannelProvider provider = new ChannelProvider(Channel.this, providerEndpointIdentity);
Channel.this.provider = provider;
callback.onSuccess(provider);
}
@Override
public void onError(Ack ack) {
// how do we let callback know if fails?
logger.error("error connecting to channel: {}", ack.getReason());
}
}, this);
}
void destroy(ChannelBase provider, AckListener ackListener) {
JSONObject payload = new JSONObject();
payload.put("channelName", provider.getChannelName());
desktopConnection.sendAction("destroy-channel", payload, new AckListener() {
@Override
public void onSuccess(Ack ack) {
Channel.this.provider = null;
ackListener.onSuccess(ack);
}
@Override
public void onError(Ack ack) {
ackListener.onError(ack);
}
}, this);
}
public CompletableFuture connectAsync() {
JSONObject payload = new JSONObject();
payload.put("channelName", Channel.this.name);
return desktopConnection.sendActionAsync("connect-to-channel", payload, Channel.this).thenApplyAsync(ack -> {
if (ack.isSuccessful()) {
JSONObject providerIdentity = (JSONObject) ack.getData();
return createChannelClient(providerIdentity, null, null);
}
else {
logger.error("error connecting to channel[{}]: {}", this.name, ack.getReason());
throw new RuntimeException(ack.getReason());
}
});
}
/**
* Connect to the channel.
* @param callback The callback that receives the wrapped {@link ChannelClient} object
*/
public void connect(AsyncCallback callback) {
try {
EventListener listener = new EventListener() {
@Override
public void eventReceived(ActionEvent actionEvent) {
if (CONNECTED_EVENT.equals(actionEvent.getType())) {
JSONObject providerIdentity = actionEvent.getEventObject();
String cname = providerIdentity.getString("channelName");
if (Channel.this.name.equals(cname)) {
createChannelClient(providerIdentity, callback, this);
}
}
}
};
this.addEventListener(CONNECTED_EVENT, listener, null);
JSONObject payload = new JSONObject();
payload.put("channelName", Channel.this.name);
desktopConnection.sendAction("connect-to-channel", payload, new AckListener() {
@Override
public void onSuccess(Ack ack) {
JSONObject providerIdentity = (JSONObject) ack.getData();
createChannelClient(providerIdentity, callback, listener);
}
@Override
public void onError(Ack ack) {
// how do we let callback know if fails?
// need to wait for connected event
logger.error("error connecting to channel: {}", ack.getReason());
}
}, this);
} catch (Exception ex) {
logger.error(String.format("Error connecting to channel %s", this.name), ex);
}
}
private synchronized ChannelClient createChannelClient(JSONObject providerIdentity, AsyncCallback callback, EventListener eventListener) {
ChannelClient client = null;
EndpointIdentity endpointIdentity = new EndpointIdentity(providerIdentity);
try {
this.removeEventListener(CONNECTED_EVENT, eventListener, null);
}
catch (DesktopException e) {
logger.error("unable to remove event listener");
}
if (!clientMap.contains(endpointIdentity)) {
client = new ChannelClient(Channel.this, endpointIdentity);
this.clientMap.put(endpointIdentity, client);
if (callback != null) {
callback.onSuccess(client);
}
fireChannelConnectEvent(endpointIdentity.getChannelId(), endpointIdentity.getUuid(), endpointIdentity.getName(), endpointIdentity.getChannelName(), endpointIdentity.getEndpointId());
} else {
logger.debug(String.format("ClientChannel already exists %s", endpointIdentity.getChannelName()));
client = clientMap.get(endpointIdentity);
callback.onSuccess(client);
}
return client;
}
void disconnect(ChannelBase client, AckListener ackListener) {
JSONObject payload = new JSONObject();
payload.put("channelName", client.getChannelName());
desktopConnection.sendAction("disconnect-from-channel", payload, new AckListener() {
@Override
public void onSuccess(Ack ack) {
if (ackListener != null) {
ackListener.onSuccess(ack);
}
fireChannelDisconnectEvent(client.getChannelId(), client.getUuid(), client.getName(),
client.getChannelName(), client.getEndpointId());
}
@Override
public void onError(Ack ack) {
ackListener.onError(ack);
}
}, this);
}
void sendChannelMessage(String action, JSONObject destionationIdentity, JSONObject providerIdentity,
JSONObject actionPayload, AckListener ackListener) {
JSONObject payload = new JSONObject(destionationIdentity,
new String[] { "name", "uuid", "channelId", "channelName", "endpointId" });
payload.put("providerIdentity", providerIdentity);
payload.put("action", action);
payload.put("payload", actionPayload);
desktopConnection.sendAction("send-channel-message", payload, ackListener,this);
}
CompletableFuture sendChannelMessageAsync(String action, JSONObject destionationIdentity, JSONObject providerIdentity,
JSONObject actionPayload) {
JSONObject payload = new JSONObject(destionationIdentity,
new String[] { "name", "uuid", "channelId", "channelName", "endpointId" });
payload.put("providerIdentity", providerIdentity);
payload.put("action", action);
payload.put("payload", actionPayload);
return desktopConnection.sendActionAsync("send-channel-message", payload, this);
}
/**
* Check if the channel has provider created
* @return if the channel provider exists.
*/
public boolean hasProvider() {
return this.provider != null;
}
/**
* Check if the channel has client connected
* @return true if a channel client exists
*/
public boolean hasClient() {
return this.clientMap.keySet().size() > 0;
}
public JSONObject invokeAction(EndpointIdentity targetIdentity, String action, JSONObject actionPayload,
JSONObject senderIdentity) {
ChannelBase target = null;
JSONObject result = null;
if (targetIdentity.getEndpointId() != null) {
//target should be channel client.
Enumeration clientKeys = this.clientMap.keys();
while (target == null && clientKeys.hasMoreElements()) {
EndpointIdentity key = clientKeys.nextElement();
if (Objects.equals(key.getEndpointId(), targetIdentity.getEndpointId())) {
ChannelClient client = this.clientMap.get(key);
if (client.hasRegisteredAction(action)) {
target = client;
}
}
}
}
else if (targetIdentity.getChannelId() != null) {
if (Objects.equals(this.provider.getChannelId(), targetIdentity.getChannelId()) && this.provider.hasRegisteredAction(action)) {
target = this.provider;
}
}
else {
if (this.provider.hasRegisteredAction(action)) {
target = this.provider;
}
else {
Enumeration clientKeys = this.clientMap.keys();
while (target == null && clientKeys.hasMoreElements()) {
EndpointIdentity key = clientKeys.nextElement();
if (Objects.equals(key.getUuid(), targetIdentity.getUuid()) && Objects.equals(key.getName(), targetIdentity.getName())) {
ChannelClient client = this.clientMap.get(key);
if (client.hasRegisteredAction(action)) {
target = client;
}
}
}
}
}
if (target != null) {
result = target.invokeAction(action, actionPayload, senderIdentity);
}
return result;
}
public boolean addChannelListener(ChannelListener listener) {
return this.channelListeners.add(listener);
}
public boolean removeChannelListener(ChannelListener listener) {
return this.channelListeners.remove(listener);
}
protected void fireChannelConnectEvent(String channelId, String uuid, String name, String channelName, String endpointId) {
ConnectionEvent event = new ConnectionEvent(channelId, uuid, name, channelName, endpointId);
for (ChannelListener listener : this.channelListeners) {
listener.onChannelConnect(event);
}
}
protected void fireChannelDisconnectEvent(String channelId, String uuid, String name, String channelName, String endpointId) {
ConnectionEvent event = new ConnectionEvent(channelId, uuid, name, channelName, endpointId);
for (ChannelListener listener : this.channelListeners) {
listener.onChannelDisconnect(event);
}
}
public void processConnection(JSONObject payload) {
JSONObject clientIdentity = payload.getJSONObject("clientIdentity");
this.provider.processConnection(clientIdentity, payload);
}
/**
* Registers an event listener on the specified event
*
* Supported system event types are:
*
*
* @param subscriptionObject A JSON object containing subscription information such as the topic and type
* @param listener EventListener for the event
* @param callback AckListener for the request
* @throws DesktopException if this method fails to add event listener specified
* @see EventListener
* @see AckListener
*/
protected void addEventListener(JSONObject subscriptionObject,
EventListener listener,
AckListener callback) throws DesktopException {
this.desktopConnection.addEventCallback(subscriptionObject, listener, callback, this);
}
/**
* Registers an event listener on the specified event
*
* Supported system event types are:
*
*
* @param type Type of the event
* @param listener EventListener for the event
* @param callback AckListener for the request
* @throws DesktopException if this method fails to add event listener specified
* @see EventListener
* @see AckListener
*/
public void addEventListener(String type, EventListener listener, AckListener callback) throws DesktopException {
try {
JSONObject eventListenerPayload = new JSONObject();
eventListenerPayload.put("topic", "channel");
eventListenerPayload.put("type", type);
addEventListener(eventListenerPayload, listener, callback);
} catch (Exception e) {
logger.error("Error adding event listener", e);
throw new DesktopException(e);
}
}
/**
* Removes a previously registered event listener from the specified event
*
* @param type Type of the event
* @param listener EventListener for the event
* @param callback AckListener for the request
* @throws DesktopException if this method fails to remove event listener specified
* @see DesktopException
* @see EventListener
* @see AckListener
*/
public void removeEventListener(String type, EventListener listener, AckListener callback) throws DesktopException{
try {
JSONObject eventListenerPayload = new JSONObject();
eventListenerPayload.put("topic", "channel");
eventListenerPayload.put("type", type);
this.desktopConnection.removeEventCallback(eventListenerPayload, listener, callback, this);
} catch (Exception e) {
logger.error("Error removing event listener", e);
throw new DesktopException(e);
}
}
}