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

com.openfin.desktop.platform.Platform Maven / Gradle / Ivy

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

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.locks.ReentrantLock;

import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.openfin.desktop.Application;
import com.openfin.desktop.DesktopConnection;
import com.openfin.desktop.DesktopException;
import com.openfin.desktop.EventListener;
import com.openfin.desktop.Identity;
import com.openfin.desktop.Window;
import com.openfin.desktop.WindowOptions;
import com.openfin.desktop.channel.Channel;
import com.openfin.desktop.channel.ChannelClient;

/**
 * Platform manages the life cycle of windows and views in the application.
 * It enables taking snapshots of itself and applying them to restore a previous configuration as well as listen to platform events.
 * @author Anthony
 *
 */
public final class Platform {
	
	private final static Logger logger = LoggerFactory.getLogger(Platform.class);

	private Application application;
	private ChannelClient channelClient;
	private ReentrantLock channelClientLock;

	private Platform(Application application) {
		this.application = application;
		this.channelClientLock = new ReentrantLock();
	}

	/**
	 * Creates and starts a Platform and returns a wrapped and running Platform instance. The wrapped Platform methods can be used to launch content into the platform. Promise will reject if the platform is already running.
	 * @param desktopConnection Connection object to the AppDesktop.
	 * @param platformOptions The required options object, also any Application option is also a valid platform option.
	 * @return new CompletionStage for the platform that was started.
	 */
	public static CompletionStage start(DesktopConnection desktopConnection, PlatformOptions platformOptions) {
		CompletableFuture platformApiReadyCallback = new CompletableFuture<>();
		logger.debug("Platform options: {}", platformOptions.getJsonCopy());
		return Application.createApplication(platformOptions, desktopConnection).thenApplyAsync(app -> {
			Platform platform = new Platform(app);
			platform.addEventListener("platform-api-ready", actionEvent -> {
				platformApiReadyCallback.complete(null);
			});
			app.runAsync();
			return platform;
		}).thenCombineAsync(platformApiReadyCallback, (platform, nothing) -> {
			return platform;
		});
	}

	/**
	 * Retrieves platforms's manifest and returns a wrapped and running Platform. If there is a snapshot in the manifest, it will be launched into the platform.
	 * @param desktopConnection Connection object to the AppDesktop.
	 * @param manifestUrl The URL of platform's manifest.
	 * @return new CompletionStage for the platform that was started.
	 */
	public static CompletionStage startFromManifest(DesktopConnection desktopConnection, String manifestUrl) {
		CompletableFuture platformApiReadyCallback = new CompletableFuture<>();
		return Application.createFromManifestAsync(manifestUrl, desktopConnection).thenApplyAsync(app -> {
			Platform platform = new Platform(app);
			platform.addEventListener("platform-api-ready", actionEvent -> {
				platformApiReadyCallback.complete(null);
			});
			app.runAsync();
			return platform;
		}).thenCombineAsync(platformApiReadyCallback, (platform, nothing) -> {
			return platform;
		});
	}

	/**
	 * Get UUID of this platform object.
	 * @return UUID of this platform object.
	 */
	public String getUuid() {
		return this.application.getUuid();
	}

	/**
	 * Add a platform event listener.
	 * @param type event name
	 * @param listener event listener to be added
	 */
	public void addEventListener(String type, EventListener listener) {
		try {
			this.application.addEventListener(type, listener, null);
		}
		catch (DesktopException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Remove the platform event listener
	 * @param type event name
	 * @param listener event listener to be removed.
	 */
	public void removeEventListener(String type, EventListener listener) {
		try {
			this.application.removeEventListener(type, listener, null);
		}
		catch (DesktopException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * Adds a snapshot to a running Platform.
	 * 
	 * Can optionally close existing windows and overwrite current platform state
	 * with that of a snapshot.
	 * 
	 * @param requestedSnapshot Url or filepath to a snapshot JSON object.
	 * @param opts Optional parameters to specify whether existing windows should be closed.
	 * @return new CompletionStage for the platform that had the snapshot applied.
	 */
	public CompletionStage applySnapshot(String requestedSnapshot, PlatformSnapshotOptions opts) {
		JSONObject payload = new JSONObject();
		payload.put("manifestUrl", requestedSnapshot);
		return this.application.getConnection().sendActionAsync("get-application-manifest", payload, this)
		.thenApplyAsync(ack->{
			if (ack.isSuccessful()) {
				return new PlatformSnapshot(ack.getJsonObject().getJSONObject("data"));
			}
			else {
				throw new RuntimeException("error retriving snapshot, reason: " + ack.getReason());
			}
		})
		.thenCompose(snapshot->{
			return this.applySnapshot(snapshot, opts);
		});
	}

	/**
	 * Adds a snapshot to a running Platform.
	 * 
	 * Can optionally close existing windows and overwrite current platform state
	 * with that of a snapshot.
	 * 
	 * @param snapshot snapshot object.
	 * @param opts Optional parameters to specify whether existing windows should be closed.
	 * @return new CompletionStage for the platform that had the snapshot applied.
	 */
	public CompletionStage applySnapshot(PlatformSnapshot snapshot, PlatformSnapshotOptions opts) {
		return this.getChannelClient().thenCompose(client -> {
			JSONObject payload = new JSONObject();
			payload.put("snapshot", snapshot.getJsonCopy());
			if (opts != null) {
				payload.put("options", opts.getJsonCopy());
			}
			return client.dispatchAsync("apply-snapshot", payload);
		}).thenApplyAsync(ack -> {
			if (ack.isSuccessful()) {
				return this;
			}
			else {
				throw new RuntimeException("error applying platform snapshot, reason: " + ack.getReason());
			}
		});
	}

	/**
	 * Closes a specified view in a target window.
	 * @param view the view to be closed.
	 * @return new CompletionStage when the command is delivered.
	 */
	public CompletionStage closeView(PlatformView view) {
		return view.getCurrentWindow().thenAcceptBothAsync(this.getChannelClient(), (win,client)->{
			JSONObject payload = new JSONObject();
			payload.put("target", win.getIdentity().getJsonCopy());
			payload.put("opts", view.getIdentity().getJsonCopy());
			client.dispatchAsync("close-view", payload);
		});
	}

	/**
	 * Creates a new view and attaches it to a specified target window.
	 * @param viewOpts View creation options
	 * @param target The window to which the new view is to be attached. If no target, create a view in a new window.
	 * @return new CompletionStage for the platform that had the view created.
	 */
	public CompletionStage createView(PlatformViewOptions viewOpts, Identity target) {
		return this.getChannelClient().thenCompose(client->{
			JSONObject payload = new JSONObject();
			payload.put("target", target == null ? null : target.getJsonCopy());
			payload.put("opts", viewOpts.getJsonCopy());
			return client.dispatchAsync("create-view", payload);
		}).thenApplyAsync(ack->{
			if (ack.isSuccessful()) {
				JSONObject result = ack.getJsonObject().getJSONObject("data").getJSONObject("result");
				return new PlatformView(new Identity(result.getJSONObject("identity")), this.application.getConnection());
			}
			else {
				throw new RuntimeException("error creating platform view, reason: " + ack.getReason());
			}
		});
	}

	/**
	 * Creates a new Window.
	 * @param winOpts Window creation options
	 * @return new CompletionStage for the platform that had the window created.
	 */
	public CompletionStage createWindow(WindowOptions winOpts) {
		return this.getChannelClient().thenCompose(client->{
			return client.dispatchAsync("create-view-container", winOpts.getJsonCopy());
		}).thenApplyAsync(ack->{
			if (ack.isSuccessful()) {
				JSONObject data = ack.getJsonObject().getJSONObject("data");
				return Window.wrap(data.getString("uuid"), data.getString("name"), this.application.getConnection());
			}
			else {
				throw new RuntimeException("error creating platform window, reason: " + ack.getReason());
			}
		});
	}

	/**
	 * Returns a snapshot of the platform in its current state.
	 * 
	 * Can be used to restore an application to a previous state.
	 * 
	 * @return new CompletionStage for the platform snapshot.
	 */
	public CompletionStage getSnapshot() {
		return this.getChannelClient().thenCompose(client -> {
			return client.dispatchAsync("get-snapshot", null);
		}).thenApplyAsync(ack->{
			if (ack.isSuccessful()) {
				return new PlatformSnapshot(ack.getJsonObject().getJSONObject("data").getJSONObject("result"));
			}
			else {
				throw new RuntimeException("error getting platform snapshot, reason: " + ack.getReason());
			}
		});
	}

	/**
	 * Reparents a specified view in a new target window.
	 * @param viewIdentity View identity
	 * @param targetIdentity New owner window identity
	 * @return new CompletionStage for the reparented view.
	 */
	public CompletionStage reparentView(Identity viewIdentity, Identity targetIdentity) {
		if (viewIdentity.getUuid() == null) {
			viewIdentity.setUuid(this.getUuid());
		}
		PlatformView view = PlatformView.wrap(viewIdentity, this.application.getConnection());
		return view.getOptions().thenComposeAsync(viewOpts -> {
			return this.createView(viewOpts, targetIdentity);
		});
	}

	/**
	 * Closes current platform, all its windows, and their views.
	 * @return new CompletionStage when the command is delivered.
	 */
	public CompletionStage quit() {
		return this.getChannelClient().thenAccept(client->{
			//can return ack actually......
			client.dispatchAsync("quit", null);
		});
	}
	
	/**
	 * Synchronously returns a Platform object that represents an existing platform.
	 * @param uuid UUID of the platform.
	 * @param desktopConnection  Connection object to the AppDesktop.
	 * @return Platform object with given UUID.
	 */
	public static Platform wrap(String uuid, DesktopConnection desktopConnection) {
		return new Platform(Application.wrap(uuid, desktopConnection));
	}

	public CompletionStage getChannelClient() {
		return CompletableFuture.supplyAsync(()->{
			try {
				if (channelClient == null) {
					channelClientLock.lock();
					if (this.channelClient == null) {
						Channel channel = new Channel("custom-frame-" + this.getUuid(), this.application.getConnection());
						channelClient = channel.connectAsync().get();
						logger.debug("platform channel client {} created", channel.getName());
					}
					this.channelClientLock.unlock();
				}
				return channelClient;
				
			}
			catch (InterruptedException | ExecutionException e) {
				throw new RuntimeException("error creating channel client: " + this.getUuid(), e);
			}
		});
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy