# Java Glue42 Reference Documentation

The Latest version of documentation can be found on Glue42 Docs.

## Using Java Glue42 Library

The Java Glue42 library provides an easy way to make your enterprise Java applications Glue42 enabled.

### Installation

You can use `java-glue42` in the same way as any standard Java library.

Java Glue42 requires JDK 8+(Java SE 8+) and is JDK 9+ ready.

#### Maven

    <?xml version="1.0" encoding="utf-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>

        <groupId>com.example</groupId>
        <artifactId>myproject</artifactId>
        <version>0.0.1-SNAPSHOT</version>

        <properties>
            <maven.compiler.source>1.8</maven.compiler.source>
            <maven.compiler.target>1.8</maven.compiler.target>
        </properties>

        <dependencies>
            <dependency>
                <groupId>com.glue42</groupId>
                <artifactId>java-glue42-shaded</artifactId>
                <version>1.4.2</version>
            </dependency>
        </dependencies>
    </project>

#### Gradle

    apply plugin: 'java'

    sourceCompatibility = 1.8
    targetCompatibility = 1.8

    dependencies {
        compile 'com.glue42:java-glue42-shaded:1.4.2'
    }

### Quick Start

    import com.tick42.glue.Glue; (1)
    try (Glue glue = Glue.builder().build()) (2)
    {
        System.out.println(glue.version()); (3)
    }

1 | First import glue package
2 | Glue is the main entry point of the sdk. It is thread-safe and you should create a single instance (per glue application) and share it throughout your code
3 | We get the Glue version string

Always close the `Glue` instance once you are done with it, in order to free up underlying resources (ws connections, thread pools…​). This example uses try-with-resources block because `Glue` extends `AutoCloseable`, in a real application, you will probably call `close` or `closeAsync` explicitly.

### Configuration

To configure Java Glue42 you can use `.conf` files, `.properties` files, `.json` files and system properties to externalize your configuration.

Here is an example minimal `glue.conf` file:

    glue {
      application: "My Java App"
    }

    Glue.builder().withApplicationName("My Java App").build();

### Application Definition

To add your Java application to the Launcher application you need to define a configuration file and add it to application config store. By default when running in file mode the store is located at `%LocalAppData%\Tick42\GlueDesktop\config\apps`.

Here is an example:

    [
      {
        "title": "Java Example",
        "type": "exe", (1)
        "name": "java-example",
        "icon": "https://enterprise-demos.tick42.com/resources/icons/w2.jpg",
        "details": {
          "path": "", (2)
          "command": "java", (3)
          "parameters": "-jar example.jar", (4)
          "mode": "tab"
        }
      }
    ]

1 | type should be exe
2 | path is the path to the application - relative or absolute
3 | command is the actual command to execute (`java`)
4 | parameters holds command line arguments

| Application definitions should be a valid JSON file, you should either use forward slash or the escape backslash

You can find detailed information about the application definitions in Configuration documentation.

### Application Shutdown

When initializing Glue42, you can pass an event listener that will notify your application when it should be shutting down. This is useful when your app/process is not started directly by Glue42, but rather by Glue42 invoking a script/batch file or another application that in turn starts your application. With this listener you can properly shut down your application, free resources, etc.

    import com.tick42.glue.Glue; (1)

    Glue.builder()
            .withShutdownRequestListener(glue -> {
                System.out.println("Starting shutdown procedure...");
                glue.closeAsync();
            }) (2)
            .build();

1 | First, import the Glue42 package.
2 | Pass an event listener for when Glue42 is about to shut down.

## Interop

### Overview

The Interop API enables applications to:

- offer functionality to other applications (JavaScript and native) by registering Interop methods

- discover applications which offer methods

- invoke (call) methods on the user’s desktop and across the network

- stream and subscribe to real-time data using the streaming API

Applications that offer methods and/or streams are called Interop servers, and applications which consume them Interop clients, and collectively - Interop instances.

#### Instance

Any application instance is identified by its Interop Instance, which is a set of known key-value pairs, uniquely identifying an application.

Here’s an example:

Key | Example | Description
--- | --- | ---
application | Client Portfolio | Application name
region | TICK42 | e.g. EU, US, AP
environment | DEMO | e.g. DEV, SIT, UAT, PROD
machine | Lambda | User’s machine name
pid | 2864 | Process ID
user | duke | Currently logged user

### Method Registration

#### Register Methods

To offer a method to other applications call `glue.interop().register()`, passing method’s definition and a callback to handle client invocations.

    glue.interop().<Map<String, Object>, Map<String, Object>>register(
            MethodDefinition.builder("Sum").withSignature("int a, int b", "int answer").build(),
            (arg, caller) -> {
                int a = (Integer) arg.get("a");
                int b = (Integer) arg.get("b");
                return Collections.singletonMap("answer", a + b);
            });

Once a method is registered, it can be invoked from any app (web, native or java), that is Glue-enabled.

#### Method Definition

The method definition describes the Interop method your application is offering. It has following properties, required ones in bold:

Name | Description
--- | ---
name | Name of the Method, e.g. OpenClientPerformance
accepts | Signature, describing the parameters that the method expects
returns | Signature, describing the properties of the object the method returns
displayName | Human-readable name of the method, e. g. Open Client Performance
description | Description of what method does, useful for documentation purposes, e.g. Launches or activates the Client Performance application
objectTypes | Entities this method is meant to work on, e.g. party(client, prospect, lead), instrument, order etc.

It’s a good idea to specify `displayName` and `description` when defining a method. These can be used by generic UI or by your own applications.

#### Signatures

The signature of a method is a comma-delimited string of parameters, defined as follows:

    type [array_modifier] [optional_modifier] parameter_name [composite_schema] [description]

Where type is one of `Bool`, `Int`, `Long`, `Double`, `String`, `DateTime`, `Composite`

Signature | Explanation
--- | ---
`String pId, String? dynamicsId` | `pId` is required, `dynamicsId` is optional
`String branchCode, String[] gIds` | `branchCode` and `gIds` are required, `gIds` is an array of strings
`Composite: {String first, String last} name` | `name` is a composite parameter, and its schema is defined by 2 required strings - `first` and `last`

`Composite` is a structure which contains one or more fields of type:

- scalar (bool, int, etc)

- array of scalars

- a composite (nested structure)

- an array of composites

Using `Composite` you can define almost any non-recursive, non-self-referential strucuture.

#### Asynchronous results

    glue.interop().registerAsync("getQuote", (arg, caller) -> {
        CompletableFuture<Map<String, Object>> future = doSomethingAsync(arg); (1)
        return future;
    });

1 | `doSomethingAsync()` returns a future

### Method Invocation

#### Invoking Methods

To invoke a method offered by other applications, call `glue.interop().invoke()`, passing method’s name and arguments. Then use the returned future to receive result or an error.

    Map<String, Object> arg = new HashMap<>();
    arg.put("a", 37);
    arg.put("b", 5);
    glue.interop().invoke("Sum", arg)
            .thenAccept(result ->
                                result.getReturned()
                                        .ifPresent(r -> System.out.println(r.get("answer"))))
            .toCompletableFuture().join();

### Multiple Responses

Invoking a method on multiple Interop instances produces multiple responses.

This is how you can iterate over all of the responses:

    result.forEach(r -> {
        if (r.getStatus().isSuccess())
        {
            System.out.printf("success:%s%n", r.getReturned().orElseGet(Collections::emptyMap));
        }
        else
        {
            System.out.printf("error:%s%n", r.getStatus().message().orElse(null));
        }
    });

| When invocation result has multiple responses the result’s `hasMultipleResponses()` will return `true`.

### Discovery

#### Discovering Methods

To list all available methods from all servers

    System.out.println(glue.interop().getMethods());

#### Searching for Methods

    include::{example-code}/InteropExample.java[tag=query,indent=0]}

#### Discovering Servers

To list all servers offering methods

    System.out.println(glue.interop().getServers());

### Streaming

#### Overview

Interop stream can be used by your application to:

- publish events which can be observed by other applications or provide real-time data (e.g. market data, news alerts, notifications etc.) to other applications, by publishing to Interop stream

- receive and react to the above events and data by creating an Interop stream subscription

We call applications which create and publish to Interop Streams publishers, and applications which subscribe to Interop Streams subscribers. An application can be both.

Interop Streams are used extensively in Glue42 Desktop products and APIs.

#### Receiving data

##### Subscribing to a Stream

Subscribing to a streaming method is achieved by invoking `glue.interop().stream()`.

    glue.interop()
            .stream("MarketData.LastTrades",
                    Collections.singletonMap("symbol", "ORCL"))
            .thenAccept(stream -> stream.subscribe(new StreamSubscriber<Map<String, Object>>() {
                @Override
                public void onData(ServerMethod method, Map<String, Object> data) {
                    // do something with the data
                }
            }));

##### Closing Stream Subscription

To close a stream subscription invoke `close()` or `closeAsync()` method on the subscription reference returned by `subscribe()` method.

    AsynchronousCloseable subscription =
            stream.subscribe(new StreamSubscriber<Map<String, Object>>() {});
    subscription.closeAsync();

##### Getting Notified when a Subscription is Closed

At any time, a stream subscription can be closed either because the publisher shutting down or due to an error.

    stream.subscribe(new StreamSubscriber<Map<String, Object>>()
    {
        @Override
        public void onSubscribe(StreamSubscription subscription)
        {
            subscription.onClose().thenRun(() -> {
                // called when the subscription is closed
            });
        }

        @Override
        public void onFailed(ServerMethod method, String reason)
        {
            // called if the subscription request is rejected
        }
    });

#### Publishing Data

To start publishing data, you need to register an Interop Stream by calling `glue.interop().register()` and provide a method definition and stream subscription request handler.

    glue.interop()
            .register(MethodDefinition.forName("Clock"),
                      StreamSubscriptionRequestHandler.accept())
            .thenAccept(stream -> {
                Map<String, Object> data =
                        Collections.singletonMap("CurrentTime", Instant.now().toEpochMilli());
                // will send data to all branches, as no branch is specified
                stream.send(data);
            });

`StreamSubscriptionRequestHandler.accept()` will accept all subscription request on the default branch (more on that later).

##### Handling Subscription Requests

To control how you application accepts or rejects stream subscription requests by specifying custom handler.

This handler receives the `request` as argument and must return a `StreamConsumer` instance via invoking `accept()`, `acceptOn()` or `reject()` methods of the `request`.

    glue.interop()
            .<Map<String, Object>>register(
                    MethodDefinition.builder("MarketData.LastTrades")
                            .withObjectType("Symbol")
                            .build(),
                    request -> {
                        String app = request.getCaller().getApplication();
                        String symbol = (String) request.getArg().get("Symbol");

                        if (symbol != null)
                        {
                            System.out.printf("Accepting %s subscription on %s%n", app, symbol);
                            return request.acceptOn(symbol);
                        }
                        else
                        {
                            System.out.printf("Rejecting %s symbol not specified%n", app);
                            return request.reject("Symbol not specified");
                        }
                    }
            );

##### Handling New or Removed Consumers

To track when a consumer is added and/removed `StreamSubscriptionRequestHandler` provides `onAdded()` and `onRemoved()` sugar methods to compose callbacks.

    glue.interop()
            .register(
                    MethodDefinition.builder("MarketData.LastTrades")
                            .withObjectType("Symbol")
                            .build(),
                    StreamSubscriptionRequestHandler
                            .<Map<String, Object>>accept((arg, caller) -> (String) arg.get("Symbol"))
                            .onAdded(consumer -> {
                                // called when new consumer subscription is accepted
                            })
                            .onRemoved(consumer -> {
                                // called when a consumer subscription is removed
                            })
            );

In order to use those methods you need an instance of type `StreamSubscriptionRequestHandler`. One way to achieve this is to not use lambda but one of the static factory methods provided.

Following fragment creates a handler that is equivalent (if you ignore logging) with the one provided in a previous section.

    StreamSubscriptionRequestHandler
            .<Map<String, Object>>accept((arg, caller) -> (String) arg.get("Symbol"))

`StreamConsumer` reference can be used to

- access request parameters and caller instance - `getArg()` and `getCaller()`

- inspect branch on which consumer was accepted - `getBranch()`

- push data directly to a consumer (unicast) - `send()`

- close the subscription forcefully - `close()` or `closeAsync()`

##### Multicast Using Branches

A single stream supports one or multiple named sub-streams that are called branches. In such cases, where it doesn’t make sense for a stream to be split into multiple sub-streams a default branch is used.

The `stream` reference is of type `StreamProducer` and can be used to:

- list all available branches:

    Map<String, StreamBranch> allBranches = stream.getBranches();

- list all stream consumers, regardless of the branch they are on:

    List<StreamConsumer<?>> allConsumers = stream.getConsumers();

If your stream publishing code uses branches (e.g. creates a branch for each unique set of subscription arguments and associates the consumers with that branch), whenever a data arrives from your underlying source, you’ll want to use the branch to publish data instead of manually walking all consumers and sending data to the interested clients.

    String branchKey = "Providers";
    StreamBranch branch = stream.getBranches().get(branchKey);

    if (branch != null)
    {
        branch.push(data);
        System.out.printf("Pushed data to all subscribers on branch \"%s\"!%n", branch.getKey());
    }
    else
    {
        System.out.printf("Branch \"%s\" does not exist!%n", branchKey);
    }

#### Stream Discovery

Streams are special Interop methods with `isSupportsStreaming()` returning `true`.

## Intents

### Overview

See docs.

### Finding Intents

To find all registered intents, use the `all()` method:

    glue.intents().all();

### Raising Intents

To raise an Intent, use the `raise()` method:

    glue.intents().raise("ShowChart");

The `raise()` method accepts an Intent name as a String or an `IntentRequest` as argument.

### Target Intent Handlers

When raising an Intent, optionally target one or more `IntentHandler` instances using the `withTarget()` method of the `IntentRequest` builder:

    Intent intent = glue.intents().all()
            .toCompletableFuture().join()
            .get("ShowChart");

    IntentHandler intentHandler = intent.getHandlers().get(0);

    IntentRequest intentRequest =
            IntentRequest.intent(intent.getName())
                    .withTarget(IntentTarget.application(intentHandler.getApplicationName()))
                    .build();

    glue.intents().raise(intentRequest);

The `IntentTarget` can be created using one of the following factory methods:

- `startNew()` - will start a new instance of the first available Intent handler;

- `reuse()` - will reuse the first available running instance of an Intent handler or fallback to `startNew()` if there are no running instances available;

- `application(String)` - will start a new instance of a specific Intent handler application;

- `instance(String)` - will reuse a specific running instance of an Intent handler;

Default value for the request’s target will be `startNew()` when an Intent Handler application is available. If the Intent has only been registered dynamically, the default value is `reuse()`.

### Context

#### Passing Initial Context

To pass initial context to the Intent handler, use the `withContext(String, T)` method of the `IntentRequest` builder.

    Map<String, Object> contextData =
            Collections.singletonMap("data",
                    Collections.singletonMap("selectedClientID", 1));

    Map<String, Object> options = new LinkedHashMap<>();
    options.put("width", 300);
    options.put("height", 200);

    glue.intents().raise(
            IntentRequest.intent("ShowChart")
                    .withTarget(IntentTarget.startNew())
                    .withContext(
                            "Instrument", // contextType
                            contextData)
                    .withOptions(options)
                    .build()
    );

The `withOptions(Map<String,Object>)` method of the `IntentRequest` builder is used to pass custom application startup options to the Intent handler.

#### Handling Context Updates

To handle the context data passed when an Intent is raised and targeted at your application, use the `addIntentListener(String, IntentContextHandler)` method.

    glue.intents().addIntentListener("ShowChart", context -> {
        context.getType().ifPresent(contextType -> {
            switch (contextType) {
                case "Instrument": {
                    Map<String, Object> data = context.getData();
                    // Application specific logic for handling the new context data
                    break;
                }
            }
        });
        return Collections.emptyMap(); // optionally return result to the caller
    });

### Registering Intents at Runtime

To register an Intent at runtime, use `addIntentListener(IntentListenerRequest, Function<IntentContext, T>)` method.

    IntentListenerRequest<Map<String, Object>> intent =
            IntentListenerRequest.intent("ShowChart", ReifiedType.OBJECT_MAP)
                    .withContextTypes(Collections.singletonList("Instrument"))
                    .withDisplayName("Instrument Chart")
                    .build();

    glue.intents().addIntentListener(intent, (context) -> {
        context.getType().ifPresent(contextType -> {
            switch (contextType) {
                case "Instrument": {
                    Map<String, Object> data = context.getData();
                    // Application specific logic for handling the new context data
                    break;
                }
            }
        });
        return Collections.emptyMap(); // optionally return result to the caller
    });

## Window Management

### Overview

Sticky Windows allows to make windows sticky, so they can stick together forming groups that can move, minimize and maximize together.

The window management API is exposed in `glue.windows()`, and gives following features, many of which not found in traditional Java GUI frameworks:

- create metro/flat or tab windows

- control and customize the chrome of these windows

  - visibility: e.g. create hidden windows, show them later

  - size: set minimum and/or maximum bounds

  - control what the user can do with the windows, e.g. allow a window to be sticky

  - add custom buttons and react to user clicking these

  - organize windows into tabs which the user can tear off

### Making a Window Sticky

#### Registering a Swing Window

    WindowManager windows = glue.windows();
    WindowHandle<JFrame> handle = windows.getWindowHandle(frame); (1)
    windows.register(handle) (2)
            .thenAccept(window ->
                    frame.addWindowListener(new WindowAdapter()
                    {
                        @Override
                        public void windowClosing(WindowEvent e)
                        {
                            window.closeAsync(); (3)
                        }
                    }));

1 | Get window handle
2 | Register window
3 | Unregister window on close

Currently, Glue42 Windows requires the underlying native handle (`hwnd` in case of Windows) to be initialized and attached to the `JFrame` before the window can be registered. This is now achieved internally through Glue32 Windows.

### Window Types

Glue supports following window modes: `flat` and `tab`.

The window type is controlled by the `mode` window option, which can be specified in the application definition or during the `register()` call

    windows.register(handle, options -> options.mode(WindowMode.FLAT));

### Controlling the Window

Once an application window is registered, Glue42 Windows will accept full control over the window positioning, sizing and visibility. The application shouldn’t use native methods (for example, Swing calls) to control the window as it will interfere with the Glue42 window management.

Glue provides several SW aware methods that should be used when direct control is required:

### Title

Change the window title:

    window.changeTitle("New Title");

#### Size and position

Change the window bounds:

    window.changeBounds(new Bounds(10, 10, 200, 200));

#### Visibility

Note that changing the window visibility also affects its associated icon.

    window.changeVisibility(false);

### Frame Buttons

You can put extra buttons in the frame area of the window and handle clicks for those buttons.

#### Adding Button

Use `addFrameButton` method to add a new button:

    window.addFrameButton("search-button",
                          ButtonOptions.builder()
                                  .toolTip("Search")
                                  .order(1)
                                  .image(new byte[0]) // needs to be a valid image
                                  .build())
            .thenRun(() -> System.out.println("created button"));

#### Removing Button

Use `removeFrameButton` to remove a button from the frame:

    window.removeFrameButton("search-button")
            .thenRun(() -> System.out.println("removed button"));

#### Handling Clicks

Use `onFrameButtonClicked` to subscribe for button clicks:

    window.onFrameButtonClicked(e -> {
        if ("search-button".equals(e.getButtonId()))
        {
            System.out.println("Search button clicked");
        }
    });

### Window hibernation/resume

You can subscribe for when the layout of your application is suspended/hibernated, so you can for example limit your resource usage while your application isn’t being used. You can also subscribe when your application is restored/resumed, so you can bring it back to it’s functional state. An example of limiting your resource usage could be for you to unsubscribe from receiving data updates and such.

With both events you’ll get the layout name and the meta data for that layout, if any.

#### Handling Layout Hibernation

    window.onHibernated(e -> System.out.println("Hibernating the layout with name " + e.getLayout()));

#### Handling Layout Resume

    window.onResumed(e -> System.out.println("Resuming the layout with name " + e.getLayout()));

## Application Management

### Overview

The Application Management API provides a way to manage Glue42 Desktop applications. Starting an application through the API returns an instance object that can be used to manage it.

ApplicationInstance - A running copy of an application within Glue42. The API provides a way to stop an application instance.

Registering a function through the API allows you to react when glue desktop wants to start a child application instance. Usually you will register a new window through the Windows API.

ApplicationInstanceHandler - A handler function that will be invoked when child application needs to be started.

### Listing Applications

To list all available applications for the current user:

    glue.appManager().applications();

This will return a `Map<String, ApplicationInstance>` as result.

### Starting applications

To start an application use the `start()` method:

    glue.appManager().start("clientlist")
            .whenComplete((instance, error) -> {
                if (error != null) {
                    // application failed to start
                }
            });

You can also pass context (an application-specific object that will be available in the new app) or override any of the pre-configured window settings:

    glue.appManager().start("clientcontact", Collections.singletonMap("selectedUser", 2));

### Listing running instances

    glue.appManager().instances();

### Stopping instances

To stop a running instance use `close()` or `closeAsync()` method:

    instance.closeAsync();

### Multi Window Apps

Java Glue42 offers support for applications consisting of multiple windows. Each child window can be registered in the context of a child Glue42 Application that you can save and restore in a Layout, start directly from the Glue42 toolbar, etc.

#### Registering Child Window Instance Handler

    glue.appManager().registerInstanceHandler("my-child-window", applicationInstance -> {

        glue.windows().register(
                WindowRegistration.builder(glue.windows().getWindowHandle(childFrame))
                        .withInstanceId(applicationInstance.getId())
                        .build()
        ).thenAccept(window ->
                window.onContextUpdated(e ->
                {
                    int selectedIndex = (Integer) e.getContext().getOrDefault("SelectedIndex", -1);
                    selector.setSelectedIndex(selectedIndex);
                }));
        glue.layouts().addSaveListener(applicationInstance.getId(), request ->
                Collections.singletonMap("SelectedIndex", selector.getSelectedIndex()));
    });

## Layouts

### Layouts Overview

Layouts allow you to save the layouts of any set of applications running in Glue Desktop as a named layout and later restore it.

You can access the API through the following entry point:

    glue.layouts();

### Global Layouts

Global save and restore is an operation in which all applications running on a user’s PCs are saved to a named layout which can then be restored.

#### Saving global layout

To save a global layout use glue.layouts().save passing a configuration object with a required property name. Note that if a layout with this name already exists it will be replaced.

    CompletionStage<Void> layout = glue.layouts().save(options -> options
            .withName("Name of Layout")
            .withType(LayoutType.GLOBAL)
    );

#### Restore global layout

To restore a layout use the restore method:

    LayoutRestoreOptions restoreOptions = LayoutRestoreOptions.builder("LayoutToRestore").build();
    CompletionStage<Void> restore = glue.layouts().restore(restoreOptions);

### Application’s default layouts

When an application is started from the Toolbar, the initial size and position is defined in the application properties. Users often want to place it at an alternative position and so move it. GlueDesktop remembers the last location of a window and use that as the new start position.

Saving the last position is enabled by default for all applications (single windows or activities), but can be disabled per application by configuration.

Also if the user holds Shift key while starting an application it will appear on default screen position (as defined in properties) – this does not require change in AppManager.

### Managing Layouts

#### Listing layouts

Listing layouts can be done by using the following method:

    Collection<Layout> layouts = glue.layouts().list();

#### Exporting layouts

To export all layouts for the current user just invoke the exportLayouts() method:

    CompletionStage<Collection<Layout>> exportedLayouts = glue.layouts().exportLayouts();

#### Importing layouts

To import layout(s) just invoke the import() method with a collection of layouts (an empty collection is being passed for the sake of the example) and a mode (MERGE or REPLACE)

    CompletionStage<Void> importLayouts = glue.layouts().importLayouts(Collections.emptyList(), LayoutImportMode.REPLACE);

#### Saving custom data

When a layout is saved applications can store custom data with it. When the layout is restored the custom data is also restored and returned to the applications.

Currently, the custom data can only be of the following type:

- window context - each window (application) can store window specific context. When restored the window will have the saved context.

To save custom data applications can subscribe for layout save requests. The callback passed as argument will be invoked when a layout save is requested.

    Map<String, Object> context = new HashMap<>();
    context.put("gridWidth", 420);
    context.put("gridHeight", 42);
    glue.layouts().addSaveListener((request) -> context);

## Channels

### Overview

The Channels API enables users to dynamically group windows, instructing them to work over the same shared data.

When two windows are on the same channel they share a context data, which they can monitor and/or update.

A context data object can contain different types of data, e.g. RIC symbol, ClientID and AccountID

    Map<String, Object> data = new LinkedHashMap<>();
    data.put("RIC", "BMW.GR");
    data.put("ClientID", 235399);
    data.put("AccountID", "X2343");

### Enable Channels for a Window

To add the channel selection to your window you need to enable the `channel` window option:

    glue.windows().register(handle, options -> options.channel())

### Create Channel Context

    ChannelContext<Map<String, Object>> channelContext = glue.channels().create(window);

### Subscribe for Channel Data

When your application wants to track the current channel and its data it should use the `subscribe()` method:

    channelContext.subscribe((ChannelContextDataSubscriber<Map<String, Object>>) (channel, data) -> {
        // each time channel context data is updated this method will be invoked
    });

### Publish Channel Data Update

To update the current channel context data use the `publish()` method:

    channelContext.publish(Collections.singletonMap("RIC", "VOD.L"));

## Shared Contexts

### Listing All Available Contexts

A shared context object is a `Map` containing cross application data. You can access all available context objects in order to manipulate them:

### Subscribing for a Context

To subscribe for shared context updates, use the `glue.contexts().subscribe()` call, passing the name of the desired context object:

    glue.contexts().subscribe("app-styling")
            .thenAccept(context -> context.data(data -> {
                // use context data here
            }));

If the specified shared context object does not exist, it will be created. In the example above, the changes to the context object can be handled in the `context.data(data → {})` callback.

### Updating a Context

You can also update a shared context object by using `glue.contexts().update()` and passing as a first parameter the name of the context object you want to update, and as a second parameter - a `Map<String, Object>` with the delta values:

    glue.contexts().update("app-styling", Collections.singletonMap("backgroundColor", "red"));

If the key you pass in the `Map` object exists in the shared context object, it will be overwritten with the new value. If it does not exist, it will be created. If the value of a key in the `Map` you pass to `glue.contexts().update()` is `null`, then that key will be deleted from the shared context object.

Let’s say you have subscribed to a shared context named `"app-styling"`, which already has two entries - `"backgroundColor", "red"` and `"alternativeColor", "yellow"`. In the example below:

- `"backgroundColor"` will be overwritten with a new value - `"blue"`;

- `"alternativeColor"` will be deleted from the shared context object;

- `"borderColor"` will be created as a new key in the shared context object and its value will be set to `"grey"`;

    glue.contexts()
            .subscribe("app-styling")
            .thenAccept(context -> {
                context.data(data -> {
                    // use context data here
                });

                Map<String, Object> delta = new HashMap<>();
                delta.put("backgroundColor", "blue");
                delta.put("alternativeColor", null);
                delta.put("borderColor", "grey");

                context.update(delta);
            });

## Notifications

### Raising Notifications

To raise a notification on user’s desktop, first create a `Notification` instance

    Notification alert = glue.notifications()
            .create("Example", notification -> notification.title("Hello from Java Glue42"));

and then call the `raise()` method

    alert.raise(NotificationSeverity.LOW);

### Actions

Notifications can contains actions (usually displayed as buttons in the UI) that the user can execute when he/she sees the notification. Executing an action results in invoking an interop method. The interop method can be registered by the notification publisher or any other application that can handle the action.

The handler of the interop action can also receive parameters, specified by the publisher of the notification.

In following example we add Accept and Reject actions, passing an `id` parameter to the first action:

    glue.interop().register("NotificationAccepted", (arg, caller) -> Collections.emptyMap());
    glue.interop().register("NotificationRejected", (arg, caller) -> Collections.emptyMap());
    glue.notifications()
            .create("Example", notification -> notification
                        .action("NotificationAccepted", "Accept", Collections.singletonMap("id", 42))
                        .action("NotificationRejected", "Reject"));

### Toast Click

When raising a notification you can specify what happens when the user clicks on the notification toasts. By default this will show the build-in notification details view, but you can replace that with invoking an interop method.

In following example, when user clicks on the notification toast the DetailsHandler interop method will be invoked

    glue.notifications()
            .create("Example",notification -> notification.details("DetailsHandler"));

## Office

### Outlook

### Prerequisites

The Outlook integration requires the Glue42 Outlook addin to be installed and running.

#### Composing Emails

While there’s no technical limitation for the Glue Outlook addin to send an email, there are many reasons while it’s not a good idea. So whenever we are talking about creating a new email, we mean that Glue for Outlook will create a new email window and populate it, but it will not send the email automatically and will instead let the user press the Send button.

To compose a new email use the compose method of the Outlook interface.

    glue.outlook().compose(message -> message
                       .to("test@test.com")
                       .cc("someone@else.com")
                       .subject("This is simply a test")
                       .body("<p>Hello from <b>Java Glue42</b></p>"));

## Appendices

### Appendix A: Reference Configuration

    glue {
      # A name that identifies the glue application instance created.
      #application: ""
      gateway: {
        # The url to use when connecting to the gateway.
        url: "ws://127.0.0.1:8385/gw"
        ws: {
          # The maximum message size.
          max-message-size: "100 MiB"
          # The maximum pool size.
          # Defaults to number of available processors
          #max-pool-size:
        }
      }
      auth: {
        # The authentication provider.
        # Allowed values are "" and win
        provider: ""
        # The authentication method.
        # Allowed values are secret, access-token and gateway-token
        method: secret
        # Configuration for the secret method
        username: ${user.name}
        password: ""
        # Configuration for access-token and gateway-token methods
        // token: ""
      }
      region: "DEMO"
      region: ${?GLUE-REGION}
      environment: "T42"
      environment: ${?GLUE-ENV}
      interop: {
        instance: true
      }
      contexts: true
      windows: {
        sticky-agent: ${glue.region}-${glue.environment}
      }
    }