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

com.openfin.desktop.fdc3.FDC3Client Maven / Gradle / Ivy

There is a newer version: 11.0.2
Show newest version
package com.openfin.desktop.fdc3;

import java.lang.System;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;

import com.openfin.desktop.*;
import org.json.JSONArray;
import org.json.JSONObject;

import com.openfin.desktop.channel.AbstractServiceClient;
import com.openfin.desktop.channel.ChannelAction;

public class FDC3Client extends AbstractServiceClient {
    private final static String Open = "OPEN";
    private final static String FindIntent = "FIND-INTENT";
    private final static String FindIntentsByContext = "FIND-INTENTS-BY-CONTEXT";
    private final static String Broadcast = "BROADCAST";
    private final static String RaiseIntent = "RAISE-INTENT";
    private final static String AddContextListener = "ADD-CONTEXT-LISTENER";
    private final static String AddIntentListener = "ADD-INTENT-LISTENER";
    private final static String RemoveIntentListener = "REMOVE-INTENT-LISTENER";
    private final static String GetDesktopChannels = "GET-DESKTOP-CHANNELS";
    private final static String GetChannelByID = "GET-CHANNEL-BY-ID";
    private final static String GetCurrentChannel = "GET-CURRENT-CHANNEL";

    private final static String ContextAction = "RECEIVE-CONTEXT";
    private final static String ChannelContextAction = "HANDLE-CHANNEL-CONTEXT";
    private final static String IntentAction = "RECEIVE-INTENT";

    private final static String ServiceChannelName = "of-fdc3-service-v1";
    private final static String Fdc3ChannelNameKey = "fdc3ChannelName";  // in response of getRuntimeInfo
    private static FDC3Client instance;

    private CopyOnWriteArrayList contextListeners;
    private ConcurrentHashMap channelContextMap;  // channelId -> context listener
    private ConcurrentHashMap intentMap;           // intent -> intent listener

    public synchronized static FDC3Client getInstance(DesktopConnection desktopConnection) {
        if (instance == null) {
            instance = new FDC3Client(desktopConnection);
        }
        return instance;
    }

    private FDC3Client(DesktopConnection desktopConnection) {
        super(ServiceChannelName, desktopConnection);
        this.contextListeners = new CopyOnWriteArrayList<>();
        this.intentMap = new ConcurrentHashMap<>();
        this.channelContextMap = new ConcurrentHashMap<>();
    }

    @Override
    public void connect(AckListener ackListener) {
        OpenFinRuntime runtime = new OpenFinRuntime(this.desktopConnection);
        try {
            runtime.getRuntimeInfo(new AckListener() {
                @Override
                public void onSuccess(Ack ack) {
                    JSONObject data = (JSONObject) ack.getData();
                    if (data.has(Fdc3ChannelNameKey)) {
                        FDC3Client.this.setChannelName(data.getString(Fdc3ChannelNameKey));
                    }
                    FDC3Client.super.connect(ackListener);
                }
                @Override
                public void onError(Ack ack) {
                    ackListener.onError(ack);
                }
            });
        } catch (Exception ex) {
            logger.error("Error getting RuntimeInfo", ex);
            DesktopUtils.errorAckOnException(ackListener, this, ex);
        }
    }

    void serviceDispatch(String action, JSONObject actionPayload, AckListener ackListener) {
        this.channelClient.dispatch(action, actionPayload, ackListener);
    }

    /**
     * Launches/links to an application by name.
     * @param name Application name
     * @param context Context object that will be provided to the opened application via a contextListener
     * @param ackListener AckListener for the request
     */
    public void open(String name, Context context, AckListener ackListener) {
        JSONObject payload = new JSONObject();
        payload.put("name", name);
        if (context != null) {
            payload.put("context", context);
        }
        this.serviceDispatch(Open, payload, ackListener);
    }

    /**
     * Publishes context to other apps on the desktop.
     * @param context Context object
     * @param ackListener AckListener for the request
     */
    public void broadcast(Context context, AckListener ackListener) {
        JSONObject payload = new JSONObject();
        payload.put("context", context);
        this.serviceDispatch(Broadcast, payload, ackListener);
    }
    
	private AppIntent parseAppIntent(JSONObject appIntentJson) {
		JSONObject intentJson = appIntentJson.getJSONObject("intent");
		IntentMetadata im = new IntentMetadata(intentJson.getString("name"), intentJson.getString("displayName"));

		JSONArray appsJson = appIntentJson.getJSONArray("apps");
		ArrayList apps = new ArrayList();
		for (int i = 0; i < appsJson.length(); i++) {
			JSONObject appObj = appsJson.getJSONObject(i);
			apps.add(new AppMetadata(appObj.getString("name")));
		}

		return new AppIntent(im, apps);
	}

    /**
     * Find out more information about a particular intent by passing its name, and optionally its context.
     * @param intent Intent name
     * @param context Context Object
     * @param callback AckListener for the request
     */
	public void findIntent(String intent, Context context, AsyncCallback callback) {
		JSONObject payload = new JSONObject();
		payload.put("intent", intent);
		if (context != null) {
			payload.put("context", context);
		}
		this.serviceDispatch(FindIntent, payload, new AckListener() {

			@Override
			public void onSuccess(Ack ack) {
				JSONObject result = ack.getJsonObject().getJSONObject("data").getJSONObject("result");
				callback.onSuccess(parseAppIntent(result));
			}

			@Override
			public void onError(Ack ack) {
			}
		});
	}

    /**
     * Find all the avalable intents for a particular context. 
     * @param context Context Object
     * @param callback AckListener for the request
     */
	public void findIntentsByContext(Context context, AsyncCallback> callback) {
		JSONObject payload = new JSONObject();
		payload.put("context", context == null ? null : context);

		this.serviceDispatch(FindIntentsByContext, payload, new AckListener() {

			@Override
			public void onSuccess(Ack ack) {
				System.out.println(ack.getJsonObject().toString());
				
				JSONArray result = ack.getJsonObject().getJSONObject("data").getJSONArray("result");
				ArrayList intents = new ArrayList();
				
				for (int i=0; i callback) {
        JSONObject payload = new JSONObject();
        payload.put("intent", intent);
        payload.put("context", context == null ? null : context);
        payload.put("target", target);

        this.serviceDispatch(RaiseIntent, payload, new AckListener() {
            @Override
            public void onSuccess(Ack ack) {
                logger.info("raise intent onSuccess, response: {}", ack.getJsonObject().toString());
                JSONObject dataObj = (JSONObject) ack.getData();
                JSONObject result = dataObj.getJSONObject("result");
                Object resultData =  result.has("data") ? result.get("data") : null;
                IntentResolution ir = new IntentResolution(result.getString("source"),resultData, result.getString("version"));
                callback.onSuccess(ir);
            }

            @Override
            public void onError(Ack ack) {
                logger.info("raise intent onError, reason: {}", ack.getReason());
            }
        });
    }

    private void registerContextAction() {
        logger.debug(String.format("Registering action %s", ContextAction));
        this.channelClient.register(ContextAction, new ChannelAction() {
            @Override
            public JSONObject invoke(String action, JSONObject payload, JSONObject senderIdentity) {
                Context context = Context.fromJson(payload.getJSONObject("context"));
                for (ContextListener listener : contextListeners) {
                    listener.onContext(context);
                }
                return null;
            }
        });
    }

    private void registerChannelContextAction() {
        logger.debug(String.format("Registering action %s", ChannelContextAction));
        this.channelClient.register(ChannelContextAction, new ChannelAction() {
            @Override
            public JSONObject invoke(String action, JSONObject payload, JSONObject senderIdentity) {
                String channelId = payload.getString("channel");
                ContextListener listener = FDC3Client.this.channelContextMap.get(channelId);
                if (listener != null) {
                    Context context = Context.fromJson(payload.getJSONObject("context"));
                    listener.onContext(context);
                    return null;
                } else {
                    logger.debug(String.format("no channel listeners for %s", channelId));
                }
                return null;
            }
        });
    }

    synchronized void addChannelContextListener(String channelId, ContextListener contextListener) {
        if (this.channelContextMap.get(channelId) == null) {
            logger.debug(String.format("Registering action %s", ChannelContextAction));
            this.channelContextMap.put(channelId, contextListener);
        } else {
            logger.debug(String.format("channel listeners already registered for %s", channelId));
        }
    }

    private void registerIntentAction() {
        logger.debug(String.format("Registering action %s", IntentAction));
        this.channelClient.register(IntentAction, new ChannelAction() {
            @Override
            public JSONObject invoke(String action, JSONObject payload, JSONObject senderIdentity) {
                String intent = payload.getString("intent");
                Context context = Context.fromJson(payload.getJSONObject("context"));
                IntentListener listener = FDC3Client.this.intentMap.get(intent);
                JSONObject result = null;
                if (listener != null) {
                    result = listener.onIntent(context);
                }
                return result;
            }
        });
    }

    @Override
    protected void onChannelConnected() {
        super.onChannelConnected();
        registerContextAction();
        registerChannelContextAction();
        registerIntentAction();
    }

    /**
     * Adds a listener for incoming context broadcast from the Desktop Agent.
     * @param callback The context listener
     * @param ackListener AckListener for the request
     */
    public void addContextListener(ContextListener callback, AckListener ackListener) {
        if (this.contextListeners.size() == 0) {
            JSONObject payload = new JSONObject();
            payload.put("id", this.channelName);
            this.serviceDispatch(AddContextListener, payload, new AckListener() {
                @Override
                public void onSuccess(Ack ack) {
                    if (ack.isSuccessful()) {
                        FDC3Client.this.contextListeners.add(callback);
                        if (ackListener != null) {
                            ackListener.onSuccess(ack);
                        }
                        logger.debug("context listener registered");
                    } else if (ackListener != null) {
                        ackListener.onError(ack);
                    }
                }
                @Override
                public void onError(Ack ack) {
                    logger.warn("unable to register context listener reason: {}", ack.getReason());
                    if (ackListener != null) {
                        ackListener.onError(ack);
                    }
                }
            });
        } else {
            FDC3Client.this.contextListeners.add(callback);
            if (ackListener != null) {
                ackListener.onSuccess(null);
            }
        }
    }

    public boolean removeContextListener(ContextListener listener) {
        return this.contextListeners.remove(listener);
    }

    /**
     * Adds a listener for incoming Intents from the Agent.
     * @param intent Intent name
     * @param listener The intent listener
     * @param ackListener AckListener for the request
     */
    public void addIntentListener(String intent, IntentListener listener, AckListener ackListener) {
        if (this.intentMap.get(intent) == null) {
            JSONObject payload = new JSONObject();
            payload.put("intent", intent);
            this.serviceDispatch(AddIntentListener, payload, new AckListener() {
                @Override
                public void onSuccess(Ack ack) {
                    if (ack.isSuccessful()) {
                        FDC3Client.this.intentMap.put(intent, listener);
                        if (ackListener != null) {
                            ackListener.onSuccess(ack);
                        }
                        logger.debug("intent {} listener registered", intent);
                    } else if (ackListener != null) {
                        ackListener.onError(ack);
                    }
                }

                @Override
                public void onError(Ack ack) {
                    logger.warn("unable to register intent listener {}, reason: {}", intent, ack.getReason());
                    if (ackListener != null) {
                        ackListener.onError(ack);
                    }
                }
            });
        } else {
            JSONObject obj = new JSONObject();
            obj.put("success", false);
            obj.put("reason", "Intent listener already registered");
            Ack ack = new Ack(obj, this);
            logger.warn("unable to register intent listener {}, reason: {}", intent, ack.getReason());
            if (ackListener != null) {
                ackListener.onError(ack);
            }
        }
    }

    void removeIntentListener(String intent, IntentListener listener, AckListener ackListener) {
        if (this.intentMap.get(intent) == listener) {
            JSONObject payload = new JSONObject();
            payload.put("intent", intent);
            this.serviceDispatch(RemoveIntentListener, payload, new AckListener() {
                @Override
                public void onSuccess(Ack ack) {
                }
                @Override
                public void onError(Ack ack) {
                }
            });
        }
    }

    public void getChannelById(String id, AsyncCallback callback) {
        JSONObject payload = new JSONObject();
        payload.put("id", id);
        this.serviceDispatch(GetChannelByID, payload, new AckListener() {
            @Override
            public void onSuccess(Ack ack) {
                if (ack.isSuccessful()) {
                    JSONObject data = ack.getJsonObject().getJSONObject("data");
                    JSONObject result = data.getJSONObject("result");
                    if (id.equals(result.getString("id"))) {
                        Channel c = new Channel(id, FDC3Client.this);
                        if (result.has("name")) {
                            c.setName(result.getString("name"));
                        }
                        else if (result.has("visualIdentity")) {
                            JSONObject vid = result.getJSONObject("visualIdentity");
                            c.setName(vid.getString("name"));
                        }
                        callback.onSuccess(c);
                    } else {
                        logger.debug("channel not found: {}", id);
                        callback.onSuccess(null);
                    }
                } else {
                    logger.debug("channel not found: {}", id);
                    callback.onSuccess(null);
                }
            }

            @Override
            public void onError(Ack ack) {
                logger.info("raise intent onError, reason: {}", ack.getReason());
                callback.onSuccess(null);
            }
        });
    }

    public void getDefaultChannel(AsyncCallback callback) {
        this.getChannelById("default", callback);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy