All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.openfin.desktop.channel.Channel Maven / Gradle / Ivy

There is a newer version: 11.0.2
Show newest version
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); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy