# [Ably](https://www.ably.io)

[![.github/workflows/check.yml](https://github.com/ably/ably-java/actions/workflows/check.yml/badge.svg)](https://github.com/ably/ably-java/actions/workflows/check.yml)
[![.github/workflows/integration-test.yml](https://github.com/ably/ably-java/actions/workflows/integration-test.yml/badge.svg)](https://github.com/ably/ably-java/actions/workflows/integration-test.yml)
[![.github/workflows/emulate.yml](https://github.com/ably/ably-java/actions/workflows/emulate.yml/badge.svg)](https://github.com/ably/ably-java/actions/workflows/emulate.yml)
[![.github/workflows/javadoc.yml](https://github.com/ably/ably-java/actions/workflows/javadoc.yml/badge.svg)](https://github.com/ably/ably-java/actions/workflows/javadoc.yml)
[![Features](https://github.com/ably/ably-java/actions/workflows/features.yml/badge.svg)](https://github.com/ably/ably-java/actions/workflows/features.yml)

_[Ably](https://ably.com) is the platform that powers synchronized digital experiences in realtime. Whether attending an event in a virtual venue, receiving realtime financial information, or monitoring live car performance data – consumers simply expect realtime digital experiences as standard. Ably provides a suite of APIs to build, extend, and deliver powerful digital experiences in realtime for more than 250 million devices across 80 countries each month. Organizations like Bloomberg, HubSpot, Verizon, and Hopin depend on Ably’s platform to offload the growing complexity of business-critical realtime data synchronization at global scale. For more information, see the [Ably documentation](https://ably.com/documentation)._

## Overview

A Java Realtime and REST client library.
This library currently targets the [Ably client library features spec](https://www.ably.com/docs/client-lib-development-guide/features/) Version 1.2.

## Installation

Include the library by adding an `implementation` reference to `dependencies` block in your [Gradle](https://gradle.org/) build script.

For [Java](https://mvnrepository.com/artifact/io.ably/ably-java/latest):

```groovy
implementation 'io.ably:ably-java:1.2.40'
```

For [Android](https://mvnrepository.com/artifact/io.ably/ably-android/latest):

```groovy
implementation 'io.ably:ably-android:1.2.40'
```

The library is hosted on [Maven Central](https://mvnrepository.com/repos/central), so you need to ensure that the repository is referenced also; IDEs will typically include this by default:

```groovy
repositories {
	mavenCentral()
}
```

We only support installation via Maven / Gradle from the Maven Central repository. If you want to use a standalone fat JAR (i.e. containing all dependencies), it can be generated via a Gradle task (see [building](#building) below), creating a "Java" (JRE) library variant only. There is no standalone / self-contained AAR build option. Checkout [requirements](#requirements).

## Runtime Requirements

The library requires that the runtime environment is able to establish a safe TLS connection (TLS v1.2 or v1.3). It will fail to connect with a `SecurityException` if this level of security is not available.

## Usage

Please refer to the [documentation](https://www.ably.com/docs) for a full API reference.

### Using the Realtime API

The examples below assume a client has been created as follows:

```java
AblyRealtime ably = new AblyRealtime("xxxxx");
```

#### Connection

AblyRealtime will attempt to connect automatically once new instance is created. Also, it offers API for listening connection state changes.

```java
ably.connection.on(new ConnectionStateListener() {
	@Override
	public void onConnectionStateChanged(ConnectionStateChange state) {
		System.out.println("New state is " + state.current.name());
		switch (state.current) {
			case connected: {
				// Successful connection
				break;
			}
			case failed: {
				// Failed connection
				break;
			}
		}
	}
});
```

#### Subscribing to a channel

Given:

```java
Channel channel = ably.channels.get("test");
```

Subscribe to all events:

```java
channel.subscribe(new MessageListener() {
	@Override
	public void onMessage(Message message) {
		System.out.println("Received `" + message.name + "` message with data: " + message.data);
	}
});
```

or subscribe to certain events:

```java
String[] events = new String[] {"event1", "event2"};
channel.subscribe(events, new MessageListener() {
	@Override
	public void onMessage(Message message) {
		System.out.println("Received `" + message.name + "` message with data: " + message.data);
	}
});
```

#### Subscribing to a channel in delta mode

Subscribing to a channel in delta mode enables [delta compression](https://www.ably.com/docs/realtime/channels/channel-parameters/deltas). This is a way for a client to subscribe to a channel so that message payloads sent contain only the difference (ie the delta) between the present message and the previous message on the channel.

Request a Vcdiff formatted delta stream using channel options when you get the channel:

```java
Map<String, String> params = new HashMap<>();
params.put("delta", "vcdiff");
ChannelOptions options = new ChannelOptions();
options.params = params;
Channel channel = ably.channels.get("test", options);
```

Beyond specifying channel options, the rest is transparent and requires no further changes to your application. The `message.data` instances that are delivered to your `MessageListener` continue to contain the values that were originally published.

If you would like to inspect the `Message` instances in order to identify whether the `data` they present was rendered from a delta message from Ably then you can see if `extras.getDelta().getFormat()` equals `"vcdiff"`.

#### Publishing to a channel

Data published to a channel (apart from strings or bytearrays) has to be instances of JsonElement to be encoded properly.

```java
// Publishing message of type String
channel.publish("greeting", "Hello World!", new CompletionListener() {
	@Override
	public void onSuccess() {
		System.out.println("Message successfully sent");
	}

	@Override
	public void onError(ErrorInfo reason) {
		System.err.println("Unable to publish message; err = " + reason.message);
	}
});

// Publishing message of type JsonElement
JsonObject jsonElement = new JsonObject();

Map<String, String> inputMap = new HashMap<String, String>();
inputMap.put("name", "Joe");
inputMap.put("surename", "Doe");

for (Map.Entry<String, String> entry : inputMap.entrySet()) {
    jsonElement.addProperty(entry.getKey(), entry.getValue());
}

channel.publish("greeting", message, new CompletionListener() {
	@Override
	public void onSuccess() {
		System.out.println("Message successfully sent");
	}

	@Override
	public void onError(ErrorInfo reason) {
		System.err.println("Unable to publish message; err = " + reason.message);
	}
});
```

#### Querying the history

```java
PaginatedResult<Message> result = channel.history(null);

System.out.println(result.items().length + " messages received in first page");
while(result.hasNext()) {
	result = result.getNext();
	System.out.println(result.items().length + " messages received in next page");
}
```

#### Presence on a channel

```java
channel.presence.enter("john.doe", new CompletionListener() {
	@Override
	public void onSuccess() {
		// Successfully entered to the channel
	}

	@Override
	public void onError(ErrorInfo reason) {
		// Failed to enter channel
	}
});
```

#### Querying the presence history

```java
PaginatedResult<PresenceMessage> result = channel.presence.history(null);

System.out.println(result.items().length + " messages received in first page");
while(result.hasNext()) {
	result = result.getNext();
	System.out.println(result.items().length + " messages received in next page");
}
```

#### Channel state

`Channel` extends `EventEmitter` that emits channel state changes, and listening those events is possible with `ChannelStateListener`

```java
ChannelStateListener listener = new ChannelStateListener() {
	@Override
  public void onChannelStateChanged(ChannelStateChange stateChange) {
    System.out.println("Channel state changed to " + stateChange.current.name());
    if (stateChange.reason != null)
        System.out.println("Channel state error" + stateChange.reason.message);
  }
};
```

You can register using

```java
channel.on(listener);
```

and after you are done listening channel state events, you can unregister using
```java
channel.off(listener);
```

If you are interested with specific events, it is possible with providing extra `ChannelState` value.

```java
channel.on(ChannelState.attached, listener);
```

#### Use of authCallback

Callback that provides either tokens (`TokenDetails`), or signed token requests (`TokenRequest`), in response to a request with given token params.

```java
ClientOptions options = new ClientOptions();

options.authCallback = new Auth.TokenCallback() {
    @Override
    public Object getTokenRequest(Auth.TokenParams params) {
        System.out.println("Token Params: " + params);
        // TODO: process params
        return null; // TODO: return TokenDetails or TokenRequest or JWT string
    }
};

AblyRealtime ablyRealtime = new AblyRealtime(options);
```

### Using the REST API

The examples below assume a client and/or channel has been created as follows:

```java
AblyRest ably = new AblyRest("xxxxx");
Channel channel = ably.channels.get("test");
```

#### Publishing a message to a channel

Given the message below

```java
Message message = new Message("myEvent", "Hello");
```

Sharing synchronously,

```java
channel.publish(message);
```

Sharing asynchronously,

```java
channel.publishAsync(message, new CompletionListener() {
  @Override
	public void onSuccess() {
	   System.out.println("Message successfully received by Ably server.");
	}

	@Override
	public void onError(ErrorInfo reason) {
		System.err.println("Unable to publish message to Ably server; err = " + reason.message);
	}
});
```

#### Querying the history

```java
PaginatedResult<Message> result = channel.history(null);

System.out.println(result.items().length + " messages received in first page");
while(result.hasNext()) {
	result = result.getNext();
	System.out.println(result.items().length + " messages received in next page");
}
```

#### Presence on a channel

```java
PaginatedResult<PresenceMessage> result = channel.presence.get(null);

System.out.println(result.items().length + " messages received in first page");
while(result.hasNext()) {
	result = result.getNext();
	System.out.println(result.items().length + " messages received in next page");
}
```

#### Querying the presence history

```java
PaginatedResult<PresenceMessage> result = channel.presence.history(null);

System.out.println(result.items().length + " messages received in first page");
while(result.hasNext()) {
	result = result.getNext();
	System.out.println(result.items().length + " messages received in next page");
}
```

#### Generate a Token and Token Request

```java
TokenDetails tokenDetails = ably.auth.requestToken(null, null);
System.out.println("Success; token = " + tokenRequest);
```

#### Fetching your application's stats

```java
PaginatedResult<Stats> stats = ably.stats(null);

System.out.println(result.items().length + " messages received in first page");
while(result.hasNext()) {
	result = result.getNext();
	System.out.println(result.items().length + " messages received in next page");
}
```

#### Fetching the Ably service time

```java
long serviceTime = ably.time();
```

#### Logging

You can get log output from the library by modifying the log level:

```java
import io.ably.lib.util.Log;

ClientOptions opts = new ClientOptions(key);
opts.logLevel = Log.VERBOSE;
AblyRest ably = new AblyRest(opts);
...
```

By default, log output will go to `System.out` for the java library, and logcat for Android.

You can redirect the log output to a logger of your own by specifying a custom log handler:

```java
import io.ably.lib.util.Log.LogHandler;

ClientOptions opts = new ClientOptions(key);
opts.logHandler = new LogHandler() {
	public void println(int severity, String tag, String msg, Throwable tr) {
		/* handle log output here ... */
	}
};
AblyRest ably = new AblyRest(opts);
...
```

Note that any logger you specify in this way has global scope - it will set as a static of the library
and will apply to all Ably library instances. If you need to release your custom logger so that it can be
garbage-collected, you need to clear that static reference:

```java
import io.ably.lib.util.Log;

Log.setHandler(null);
```

#### Threads

AblyRealtime will invoke all callbacks on background thread.
If you are using Ably in Android application you must switch to main thread to update UI.

```java
channel.presence.enter("john.doe", new CompletionListener() {
    @Override
    public void onSuccess() {
        //If you are in Activity
        runOnUiThread(new Runnable() {
        @Override
        public void run() {
                //Update your UI here
            }
        });

        //If you are in Fragment or other class
        Handler handler = new Handler(Looper.getMainLooper());
        handler.post(new Runnable() {
            @Override
            public void run() {
                //Update your UI here
            }
        });
    }
});
```

### Using the Push API

#### Delivering push notifications

See [documentation](https://www.ably.com/docs/general/push/publish)  for detail.

Ably provides two models for delivering push notifications to devices.

To publish a message to a channel including a push payload:

```java
Message message = new Message("example", "realtime data");
message.extras = io.ably.lib.util.JsonUtils.object()
    .add("push", io.ably.lib.util.JsonUtils.object()
        .add("notification", io.ably.lib.util.JsonUtils.object()
            .add("title", "Hello from Ably!")
            .add("body", "Example push notification from Ably."))
        .add("data", io.ably.lib.util.JsonUtils.object()
            .add("foo", "bar")
            .add("baz", "qux")));

rest.channels.get("pushenabled:foo").publishAsync(message, new CompletionListener() {
    @Override
    public void onSuccess() {}

    @Override
    public void onError(ErrorInfo errorInfo) {
        // Handle error.
    }
});
```

To publish a push payload directly to a registered device:

```java
Param[] recipient = new Param[]{new Param("deviceId", "xxxxxxxxxxx");

JsonObject payload = io.ably.lib.util.JsonUtils.object()
        .add("notification", io.ably.lib.util.JsonUtils.object()
            .add("title", "Hello from Ably!")
            .add("body", "Example push notification from Ably."))
        .add("data", io.ably.lib.util.JsonUtils.object()
            .add("foo", "bar")
            .add("baz", "qux")));

rest.push.admin.publishAsync(recipient, payload, , new CompletionListener() {
	 @Override
	 public void onSuccess() {}

	 @Override
	 public void onError(ErrorInfo errorInfo) {
		 // Handle error.
	 }
 });
```

#### Activating a device and receiving notifications (Android only)

See https://www.ably.com/docs/general/push/activate-subscribe for detail.
In order to enable an app as a recipient of Ably push messages:

- register your app with Firebase Cloud Messaging (FCM) and configure the FCM credentials in the app dashboard;
- Implement a service extending [`FirebaseMessagingService`](https://firebase.google.com/docs/reference/android/com/google/firebase/messaging/FirebaseMessagingService) and ensure it is declared in your `AndroidManifest.xml`, as per [Firebase's guide: Edit your app manifest](https://firebase.google.com/docs/cloud-messaging/android/client#manifest);
  - Override [`onNewToken`](https://firebase.google.com/docs/reference/android/com/google/firebase/messaging/FirebaseMessagingService#public-void-onnewtoken-string-token), and provide Ably with the registration token: `ActivationContext.getActivationContext(this).onNewRegistrationToken(RegistrationToken.Type.FCM, token);`. This method will be called whenever a new token is provided by Android.
- Activate the device for push notifications:

```java
realtime.setAndroidContext(context);
realtime.push.activate();
```

## Resources

Visit https://www.ably.com/docs for a complete API reference and more examples.

### Example projects:

- [Ably Asset Tracking SDKs for Android](https://github.com/ably/ably-asset-tracking-android/blob/main/README.md#useful-resources)
- [Chat app using Spring Boot + Auth0 + Ably](https://github.com/ably-labs/spring-boot-auth0)
- [Spring + Ably Pub/Sub Demo with a Collaborative TODO list](https://github.com/ably-labs/ably-spring-pubsub)

## Requirements

For Java, JRE 8 or later is required. Note that the [Java Unlimited JCE extensions](https://www.oracle.com/uk/java/technologies/javase-jce8-downloads.html) must be installed in the Java runtime environment.

For Android, 4.4 KitKat (API level 19) or later is required.

## Support, feedback and troubleshooting

Please visit http://support.ably.io/ for access to our knowledgebase and to ask for any assistance.

You can also view the [community reported Github issues](https://github.com/ably/ably-java/issues).

To see what has changed in recent versions of Bundler, see the [CHANGELOG](CHANGELOG.md).

## Contributing

For guidance on how to contribute to this project, see [CONTRIBUTING.md](CONTRIBUTING.md).


[![Build Status](https://github.com/stevespringett/Alpine/workflows/Maven%20CI/badge.svg)](https://github.com/stevespringett/Alpine/actions?workflow=Maven+CI)
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/us.springett/alpine-parent/badge.svg)](https://maven-badges.herokuapp.com/maven-central/us.springett/alpine-parent)
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/cefa2866cbc24deeb7fbc83b8f71ad60)](https://www.codacy.com/app/stevespringett/Alpine?utm_source=github.com&utm_medium=referral&utm_content=stevespringett/Alpine&utm_campaign=Badge_Grade)
[![Dependency Status](https://img.shields.io/librariesio/github/stevespringett/Alpine.svg)](https://libraries.io/github/stevespringett/Alpine)
[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/690/badge)](https://bestpractices.coreinfrastructure.org/projects/690)
[![License][license-image]][license-url]
[![Join the chat at https://gitter.im/java-alpine/Lobby](https://badges.gitter.im/java-alpine/Lobby.svg)](https://gitter.im/java-alpine/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
![Static Analysis][fortify-image]
[![Component Analysis][sca-image]][sca-url]
<img src="http://stevespringett.github.io/alpine/images/Alpine.svg" width="300" align="right">

Alpine
=========

An opinionated scaffolding library that jump-starts Java projects with an
API-first design, secure defaults, and minimal dependencies. Alpine came
about due to many commonalities between several of my personal and
professional projects.

Alpine provides the basis for quickly developing a '**Thin Server Architecture**'.
This type of architecture shifts the role of webapps to being API providers
with little or no responsibility for server-side HTML rendering. This type
of architecture is perfect for client-side rendered webapps that rely heavily
on JSON, for Single Page Applications (SPA), and to power back-ends that drive
mobile applications.

Design Features
-

* **API-First Design** -
Alpine assumes an API-first design where REST (via JAX-RS) is
at its core. The servers resources are stateless and do not rely
on sessions. JSON Web Tokens (JWT) are used to maintain some state
and are signed with an HMAC.

* **Authentications** -
Alpine supports multiple types of principals including LDAP users and
API keys, both of which can be integrated into teams for access control.

* **Simplified Event System** -
Alpine can perform parallel processing of tasks using an asynchronous
pub/sub model. It can easily scale to consume as much or as little of
available resources as necessary.

* **Secure By Default** -
All REST resources are secure by default, requiring authentication to
access them. Gone are the days of forgetting to protect a resource.

* **Flexible Persistences** -
Uses JDO, the most flexible Java persistence specification available. I
never understood how JPA, a small subset of JDO, is viewed as progress.
It's not.

* **Minimal Dependencies** -
Too many frameworks unnecessary increase the attack surface of applications
built using them. Even a simple Hello World application is often susceptible
to attack from the use of vulnerable components or poorly configured
frameworks. Alpine includes what is necessary for a modern app, nothing more.

* **Control** -
Frameworks often force developers higher up the stack, freeing them from
low-level details. While this is certainly a huge win for productivity,
developers often don't understand how their app actually works. Alpine
does not do this. It provides standards-based APIs in a pre-packaged
library giving developers both full control over their app, as well as a
productivity jumpstart.

Application Features
-

The following features are free and require little or no coding just for using Alpine.
* Authentication for Internal (managed) and LDAP users
* Authentication via API keys
* Authentication via JWT
* Stateless API-first design
* REST resources are locked down by default (requires authentication)
* Configurable enforcement of authentication and authorization
* Built-in support for BCrypt for the hashing and salting of passwords for managed users
* Built-in models for managed users, LDAP users, API keys, and groups (called teams in Alpine)
* Built-in and consistent support for pagination and ordering via REST
* Embedded database
* Flexible persistence supporting RDBMS and non-RDBMS datastores (via Datanucleus JDO)
* Separate application and audit logs
* Scheduled and on-demand execution of parallel tasks via asynchronous pub/sub event framework
* Extendable and centralized application configuration
* Lightweight upgrade framework can optionally perform automatic upgrades on startup
* Built-in input validation (JSR 303 & 349) for all REST resources and default model classes
* Defensive security mechanisms for enabling:
  * Click-jacking protection (X-Frame-Options) (RFC-7034)
  * Content Security Policy (Level 1 and 2)
  * HTTP Public Key Pinning (HPKP) (RFC-7469)
  * HTTP Strict Transport Security (HSTS) (RFC-6797)
  * Whitelist and Blacklist URL filters

Build Features
-

These build-time features are inherited simply by using the Alpine pom
* Simplifies dependency management. Simply including Alpine as a dependency is all that's required
* Analysis of third-party components for known vulnerabilities via Sonatype OSS Index & Retire.js
* Automatic creation of CycloneDX BoM incorporating all direct and transitive dependencies used
* Support for HPE Fortify Source Code Analyzer (SCA) (requires Fortify license to use)
* Alpine apps are automatically built as WARs
* Optional packaging as an executable WAR containing an embedded Jetty container

Compiling
-------------------

```bash
mvn clean install
````

Maven Usage
-------------------

```xml
<!-- Place the parent right after the <project> root node
     to inherit all the goodies from alpine-parent pom -->
<parent>
    <groupId>us.springett</groupId>
    <artifactId>alpine-parent</artifactId>
    <version>1.10.4</version>
</parent>

<dependencies>
    <!-- Add the alpine dependency -->
    <dependency>
        <groupId>us.springett</groupId>
        <artifactId>alpine</artifactId>
        <version>1.10.4</version>
    </dependency>
</dependencies>
```

Copyright & License
-

Alpine is Copyright (c) Steve Springett. All Rights Reserved.

Permission to modify and redistribute is granted under the terms of the
[Apache License 2.0](https://github.com/stevespringett/alpine/blob/master/LICENSE.txt)

Alpine makes use of several other open source libraries. Please see
the [NOTICE.txt](https://github.com/stevespringett/alpine/blob/master/NOTICE.txt) file for more information.

  [alpine-image]: http://6000rpms.com/images/Alpine.svg
  [GitHub Wiki]: https://github.com/stevespringett/alpine/wiki
  [license-image]: https://img.shields.io/badge/license-apache%20v2-brightgreen.svg
  [license-url]: https://github.com/stevespringett/alpine/blob/master/LICENSE.txt
  [fortify-image]: https://img.shields.io/badge/static%20analysis-fortify%20sca-blue.svg
  [sca-image]: https://img.shields.io/badge/component%20analysis-oss%20index-blue.svg
  [sca-url]: https://ossindex.sonatype.org/


# FAF API

[![Codacy Badge](https://app.codacy.com/project/badge/Grade/9ac3915f155c4bfb869b8fe123b6832e)](https://www.codacy.com/gh/FAForever/faf-java-api/dashboard?utm_source=github.com&utm_medium=referral&utm_content=FAForever/faf-java-api&utm_campaign=Badge_Grade)
[![Build](https://github.com/FAForever/faf-java-api/actions/workflows/build.yaml/badge.svg)](https://github.com/FAForever/faf-java-api/actions/workflows/build.yaml)
[![Coverage Status](https://coveralls.io/repos/github/FAForever/faf-java-api/badge.svg?branch=develop)](https://coveralls.io/github/FAForever/faf-java-api?branch=develop)

This is the official FAForever API. Amongst others, the API offers the following functionality:
- User management (registration, password reset, etc.)
- JSON-API compliant REST API for lots of community data (i.e. map, mod & replay vault, game statistics, etc.)
- Leaderboards
- Clan management
- Vault upload functionality
- Internal FAF featured mod deployments
- Challonge proxy

## How to run

### Setup database

The application requires a database scheme in the right version. To create this database please checkout the project [faf-stack](https://github.com/FAForever/faf-stack), open a shell terminal (git bash on Windows) and run the script `scripts/init-db.sh`. This will setup the database in the latest version and configure the users for you.

### From source

In order to run the application from source code:

1. Clone the repository
1. Import the project into IntelliJ. For some reason, IntelliJ deletes launch configurations after import. Please revert such deleted files first (Version Control (Alt+F9) -> Local Changes)
1. Configure your JDK if you haven't already
1. Make sure you have the _IntelliJ Lombok plugin_ installed
1. Set up a [FAF database](https://github.com/FAForever/db).
1. Launch `FafApiApplication`

### From binary

Given the number of required configuration values, it's easiest to run the API using [faf-stack](https://github.com/FAForever/faf-stack):

    docker-compose up -d faf-java-api

## Database dependency

Due to potential issues with Hibernate, the application checks on startup if the flyway migration version matches the expected version and otherwise fails to boot.

* To increment the db version you need to change it in two places:
  * application.yml: `faf-api.database.schema-version` (just an integer of the version)
  * .github/workflows/build.yaml: Global variable `FAF_DB_VERSION` (git tag of the db release, usually prefixed with a `v`)
* In some cases it might make sense to override the required version on the server. Use the env variable `DATABASE_SCHEMA_VERSION`

## Sample routes

* [API documentation](http://localhost:8010)
* [List event definitions](http://localhost:8010/data/event)
* [List 5 maps with more than 8 players](http://localhost:8010/data/mapVersion?filter=(maxPlayers=gt=8)&page[size]=5)
* [List UI mods, sorted by last updated ascending](http://localhost:8010/data/modVersion?filter=(type=='UI')&sort=-updateTime)
* [List all players](http://localhost:8010/data/player)
* [List events of players](http://localhost:8010/data/playerEvent)
* [List replays](http://localhost:8010/data/game?filter=(endTime=isnull=true))

## Technology Stack

This project uses:

* Java 17 as the programming language
* [Spring Boot](https://projects.spring.io/spring-boot/) as a framework
* [Hibernate ORM](http://hibernate.org/orm/) as ORM mapper
* [Elide](http://elide.io/) with [RSQL filters](http://elide.io/pages/guide/08-filters.html#rsql) to serve [JSON-API](http://jsonapi.org/) conform data
* [Gradle](https://gradle.org/) as a build automation tool
* [Docker](https://www.docker.com/) to deploy and run the application
* [Swagger](http://swagger.io/) as an API documentation tool


////
DO NOT EDIT THIS FILE. IT WAS GENERATED.
Manual changes to this file will be lost when it is generated again.
Edit the files in the src/main/asciidoc/ directory instead.
////

image::https://github.com/spring-cloud/spring-cloud-consul/workflows/Build/badge.svg?style=svg["Actions Status", link="https://github.com/spring-cloud/spring-cloud-consul/actions"]
image::https://codecov.io/gh/spring-cloud/spring-cloud-consul/branch/main/graph/badge.svg["Codecov", link="https://codecov.io/gh/spring-cloud/spring-cloud-consul/branch/main"]

[[quick-start]]
= Quick Start

[[consul-overview]]
= Consul overview

Features of Consul

* Distributed configuration
* Service registration and discovery
* Distributed events
* Distributed locking and sessions
* Supports multiple data centers
* Built in, user-friendly user interface

See the https://consul.io/intro/index.html[intro] for more information.

[[spring-cloud-consul-features]]
= Spring Cloud Consul Features

* Spring Cloud `DiscoveryClient` implementation
** supports Spring Cloud Gateway
** supports Spring Cloud LoadBalancer
* Consul based `PropertySource` loaded during the 'bootstrap' phase.
* Spring Cloud Bus implementation based on Consul https://www.consul.io/docs/agent/http/event.html[events]

[[running-the-sample]]
= Running the sample

1. Run `docker-compose up`
2. Verify consul is running by visiting http://localhost:8500
3. Run `mvn package` this will bring in the required spring cloud maven repositories and build
4. Run `java -jar spring-cloud-consul-sample/target/spring-cloud-consul-sample-${VERSION}.jar`
5. visit http://localhost:8080, verify that `{"serviceId":"<yourhost>:8080","host":"<yourhost>","port":8080}` results
6. run `java -jar spring-cloud-consul-sample/target/spring-cloud-consul-sample-${VERSION}.jar --server.port=8081`
7. visit http://localhost:8080 again, verify that `{"serviceId":"<yourhost>:8081","host":"<yourhost>","port":8081}` eventually shows up in the results in a round robbin fashion (may take a minute or so).

[[building]]
= Building

[[building]]
= Building

Unresolved directive in https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/main/docs/modules/ROOT/pages/building.adoc - include::partial$building.adoc[]

[[contributing]]
= Contributing

[[contributing]]
= Contributing

Unresolved directive in https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/main/docs/modules/ROOT/pages/contributing.adoc - include::partial$contributing.adoc[]


# Bytecoder

Bytecoder is a Rich Domain Model for Java Bytecode and Framework to interpret and transpile it to other
languages such as JavaScript, OpenCL or WebAssembly.

Current Build status: ![Build Workflow](https://github.com/mirkosertic/Bytecoder/workflows/Build%20Workflow/badge.svg) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/de.mirkosertic.bytecoder/bytecoder-parent/badge.svg)](https://maven-badges.herokuapp.com/maven-central/de.mirkosertic.bytecoder/bytecoder-parent/badge.svg)

## High Level Goals

* Ability to cross-compile JVM Bytecode to JavaScript, WebAssembly, OpenCL and other languages
* Primary compile targets are JavaScript and WebAssembly
* Supports Java 8 up to Java 20
* Work well with Debugger Toolchains and SourceMaps
* Allow integration with other UI-Frameworks such as vue.js
* Backed by OpenJDK 20 as JRE Classlib

## Compiling strategies

The JVM Bytecode is parsed and transformed into an intermediate representation. This intermediate representation is passed thru
optimizer stages and sent to a backend implementation for target code generation.

The **JavaScript** backend transforms the intermediate representation into JavaScript.

The **WebAssembly** backend transforms the intermediate representation into WebAssembly text and binary code.

The **OpenCL** backend is used to compile single algorithms into OpenCL and execute them on the GPU. This backend is designed to enhance
existing programs running on the JVM to utilize the vast power of modern GPUs.

## User Manual

The Bytecoder User Manual and a full set of example applications is available [here](https://mirkosertic.github.io/Bytecoder/).


# Maven Plugins for Azure Services
[![Build Status](https://dev.azure.com/mseng/VSJava/_apis/build/status/Azure-Maven-Plugins/Azure-Maven-Plugins-CI?branchName=develop)](https://dev.azure.com/mseng/VSJava/_build/latest?definitionId=9232&branchName=develop)
[![Gitter](https://badges.gitter.im/Microsoft/azure-tools-for-java.svg)](https://gitter.im/Microsoft/azure-tools-for-java?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)

This repository contains all Maven plugins for Microsoft Azure services.

* [Plugins](#plugins)
* [Authentication](#Authentication)
* [Common Configurations](#Common-Configurations)
* [CI/CD in Azure DevOps](#CI-CD-in-Azure-DevOps)
* [Feedback and Questions](#Feedback-and-Questions)
* [Contributing](#Contributing)
* [Telemetry](#Telemetry)

## Related repository: azure-maven-archetypes

You may be also interested in the [Azure Maven Archetypes](https://github.com/microsoft/azure-maven-archetypes). These plugins are very closely related to the archetypes, and both kinds of artifacts work together to give you Java your way on Azure.

For more information on authentication, common configurations, CI CD, and general plugin documentation, [see the Wiki](https://github.com/microsoft/azure-maven-plugins/wiki).

## Plugins

Maven Plugin | Maven Central Version | Build Status
---|---|---
[Maven Plugin for Azure Web Apps](./azure-webapp-maven-plugin/README.md) | [![Maven Central](https://img.shields.io/maven-central/v/com.microsoft.azure/azure-webapp-maven-plugin.svg)](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.microsoft.azure%22%20AND%20a%3A%22azure-webapp-maven-plugin%22) | [![AppVeyor Webapp Plugin](https://ci.appveyor.com/api/projects/status/0vr4svfgl9u3rcaw/branch/develop?svg=true)](https://ci.appveyor.com/project/xscript/azure-maven-plugins-xt3xm)
[Maven Plugin for Azure Functions](https://github.com/microsoft/azure-maven-plugins/wiki/Azure-Functions) | [![Maven Central](https://img.shields.io/maven-central/v/com.microsoft.azure/azure-functions-maven-plugin.svg)](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.microsoft.azure%22%20AND%20a%3A%22azure-functions-maven-plugin%22) | [![AppVeyor Function Plugin](https://ci.appveyor.com/api/projects/status/5jti4qwh0j4ekh72/branch/develop?svg=true)](https://ci.appveyor.com/project/xscript/azure-maven-plugins-vvy0i)
[Maven Plugin for Azure Spring Apps](https://github.com/microsoft/azure-maven-plugins/wiki/Azure-Spring-Cloud) | [![Maven Central](https://img.shields.io/maven-central/v/com.microsoft.azure/azure-spring-apps-maven-plugin.svg)](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.microsoft.azure%22%20AND%20a%3A%22azure-spring-apps-maven-plugin%22) |

## Authentication

All the Azure Maven plugins share the same authentication logic. There are 4 authentication methods by priority order:

1. [Service Principles in plugin configuration](https://github.com/microsoft/azure-maven-plugins/wiki/Authentication#service-principles-in-plugin-configuration)
1. [Service Principles in settings.xml](https://github.com/microsoft/azure-maven-plugins/wiki/Authentication#service-principles-in-settings.xml) (Recommended for production use)
1. [Maven Plugin for Azure Account](https://github.com/microsoft/azure-maven-plugins/wiki/Authentication#maven-plugin-for-azure-account) (Default if no other method are used)
1. [Azure CLI](https://github.com/microsoft/azure-maven-plugins/wiki/Authentication#azure-cli)

For example, if you have not only Service Principles configured in your plugin configuration, but also Azure CLI installed and logged in, the Azure Maven plugins will use the Service Principles in your plugin configuration.

If no credential found, Azure Maven plugins will automatically log you in with the third method like OAuth or DeviceLogin provided by Maven Plugin for Azure Account.

### AuthType (since Web App 1.9.0)
You can specify which authentication method to use with <authType> in Maven configuration, the default value is auto, and here are all the valid values:

* service_principal
    * Will use credential specified in plugin configuration or Maven settings.xml, this is also the first priority authentication method in auto
* azure_auth_maven_plugin
    * Will use credential provided by Azure Auth Maven Plugin, it will first consume existing secret files, and will guide you auth with Oath or Device Login if you hadn't authenticated with Auth Maven Plugin before.
* azure_cli
    * Will use credential provided by Azure CLI, this could also be used in Azure Cloud Shell.
* auto
    * Will try all the auth methods in the following sequence: service_principal, azure_auth_maven_plugin(existing secret files), azure_cli, azure_auth_maven_plugin

> Maven plugin will only try the specific authentication method (except auto) if <AuthType> is set in configuration.

See the [Authentication](https://github.com/microsoft/azure-maven-plugins/wiki/Authentication) section in our wiki for more information.

## Common Configurations
The three Maven Plugins for Azure Web App/Functions/Spring Apps support below configuration properties.

| Property | Required | Description | Version |
| --- | --- | --- | --- |
| \<subscriptionId> | false	| Specifies the target subscription.
Use this setting when you have multiple subscriptions in your authentication file. | WebApp: 0.1.0
Function: 0.1.0
Spring: 1.0.0 |
| \<allowTelemetry> | false | Specifies whether to allow this plugin to send telemetry data; default value is true. | 	WebApp: 0.1.0
 Function: 0.1.0
 Spring: 1.0.0 |
| \<auth> | false | Specifies auth configuration. For more info, please refer to [here.](https://github.com/microsoft/azure-maven-plugins/wiki/Authentication#authentication) | WebApp:0.1.0
 Function:0.1.0 |
 | \<authType> | false | Specifies which authentication method to use, default value is auto. For more infos, please refer to [here.](https://github.com/microsoft/azure-maven-plugins/wiki/Authentication#authtype) | WebApp:1.9.0 |
 | \<skip> | false | Specifies whether to skip execution. Default value is false. | WebApp: 0.1.4
 Function: 0.1.0 |

## Feedback and Questions
To report bugs or request new features, file issues on [Issues](https://github.com/microsoft/azure-maven-plugins/issues). Or, ask questions on [Stack Overflow with tag azure-java-tools](https://stackoverflow.com/questions/tagged/azure-java-tools).

## Contributing

This project welcomes contributions and suggestions.  Most contributions require you to agree to a
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
the rights to use your contribution. For details, visit https://cla.microsoft.com.

When you submit a pull request, a CLA-bot will automatically determine whether you need to provide
a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions
provided by the bot. You will only need to do this once in all repositories using our CLA.

This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.

## Telemetry
This project collects usage data and sends it to Microsoft to help improve our products and services.
Read Microsoft's [privacy statement](https://privacy.microsoft.com/en-us/privacystatement) to learn more.
If you would like to opt out of sending telemetry data to Microsoft, you can set `allowTelemetry` to false in the plugin configuration.
Please read our [documents](https://aka.ms/azure-maven-config) to find more details.


# Android-nRF-Mesh-Library
[ ![Download](https://maven-badges.herokuapp.com/maven-central/no.nordicsemi.android/mesh/badge.svg?style=plastic) ](https://search.maven.org/artifact/no.nordicsemi.android/mesh)

## About
The Android-nRF-Mesh-Library allows provisioning and sending messages to Bluetooth Mesh devices.

> Bluetooth Mesh specification may be found here: https://www.bluetooth.com/specifications/mesh-specifications/

The library is compatible with version 1.0.1 of the Bluetooth Mesh Profile Specification.

nRF Mesh for Android is supported on Android devices running Android 4.3 and onwards.
## Features

The library is compatible with
- **Mesh Profile 1.0.1**,
- **Mesh Model 1.0.1**,
- **Mesh Device Properties 2**.

The mesh network configuration (JSON schema) is compatible with
- **Mesh Configuration Database Profile 1.0**.

Bluetooth mesh specifications are available at
[Bluetooth.com](https://www.bluetooth.com/specifications/specs/?status=active&show_latest_version=1&keyword=mesh).

### Supported features
1. Provisioning with all features that available in Bluetooth Mesh Profile 1.0.1, including
   OOB Public Key and all types of OOB.
2. Managing Provisioners, Network Keys, Application Keys, resetting network, etc.
3. All network layers are working.
4. Parsing Secure Network beacons.
5. Adding, removing and refreshing Network and Application Keys to Nodes.
6. Binding and unbinding Application Keys to Models.
7. Setting and clearing publication to a Model.
8. Setting and removing subscriptions to a Model.
9. Groups, including those with Virtual Addresses.
10. UI for controlling groups (Generic OnOff and Generic Level (delta) are supported).
12. Handling Configuration Server message sent by other Provisioner.
13. Generic OnOff and Vendor model have dedicated controls in sample app.
14. Proxy Filter.
15. IV Index update (handling updates received in Secure Network beacons).

## Requirements

* Android Studio
* An Android device with BLE capabilities

## Optional

* nRF5 based Development Kit(s) to test the sample firmwares on.

## Installation

* Open 'Android-nRF-Mesh-Library' project using Android Studio.
* Connect an Android device.
* Build and run project.
* To be able to quickly start testing, use the bundled firmwares directory named `ExampleFirmwares`
that includes a light server (Light source) and a light client (Switch) firmwares. those firmwares
will work on a `nrf52832` DevKit.

## How to include it in your own project

#### Maven Central

The library may be found on the Maven Central repository.
Add it to your project by adding the following dependency:

```groovy
implementation 'no.nordicsemi.android:mesh:3.3.4'
```

#### Manual

Clone this project and add *ble* module as a dependency to your project:

1. In *settings.gradle* file add the following lines:
```groovy
include ':mesh'
```

2. In *app/build.gradle* file add `implementation project(':mesh')` inside dependencies.
3. Sync project and build it.

See example projects in this repository.

#### Sample

To start using the library in your own project take a look at the following snippet.
```java
        MeshManagerApi mMeshManagerApi = new MeshManagerApi(context);
        mMeshManagerApi.setMeshManagerCallbacks(this);
        mMeshManagerApi.setProvisioningStatusCallbacks(this);
        mMeshManagerApi.setMeshStatusCallbacks(this);
        mMeshManagerApi.loadMeshNetwork();
```

The sample application uses the [Android BLE Library](https://github.com/NordicSemiconductor/Android-BLE-Library/)
by Nordic Semiconductor ASA and is recommended to use this dependency in your application.
Follow the snippet below when using the Android-Ble-Library in combination with the Android-Mesh-Library
to send and receive data.
```java
    @Override
    public void onDataReceived(final BluetoothDevice bluetoothDevice, final int mtu, final byte[] pdu) {
        mMeshManagerApi.handleNotifications(mtu, pdu);
    }

    @Override
    public void onDataSent(final BluetoothDevice device, final int mtu, final byte[] pdu) {
        mMeshManagerApi.handleWriteCallbacks(mtu, pdu);
    }
```
When using your own ble library/module call the `mMeshManagerApi.handleNotifications(mtu, pdu);` and
`mMeshManagerApi.handleWriteCallbacks(mtu, pdu);` to send and receive data.

Provisioning a node in to the network can be done in three steps,

1. Connect to the node advertising with the Mesh Provisioning UUID
2. Identify the node to be provisioned, where the devices will blink, vibrate or beep depending on
the capabilities for pre-defined duration of 5 seconds. This is useful when provisioning multiple nodes.
To identify a node call
```java
    void identifyNode(@NonNull final UUID deviceUUID) throws IllegalArgumentException;
```
by passing the device uuid of the unprovisioned mesh node.
or call
```java
    void identifyNode(@NonNull final UUID deviceUUID, final int attentionTimer) throws IllegalArgumentException;
```
by passing the device uuid of the unprovisioned mesh node and the desired duration.
3. Depending on the identified device capabilities, call one of the following functions to provision
   the node.
```java
    void startProvisioning(
        @NonNull final UnprovisionedMeshNode unprovisionedMeshNode
    ) throws IllegalArgumentException;
```
or
```java
    void startProvisioningWithStaticOOB(
        @NonNull final UnprovisionedMeshNode unprovisionedMeshNode
    ) throws IllegalArgumentException;
```
or
```java
    void startProvisioningWithOutputOOB(
        @NonNull final UnprovisionedMeshNode unprovisionedMeshNode,
        @NonNull final OutputOOBAction oobAction
    ) throws IllegalArgumentException;
```
or
```java
    void startProvisioningWithInputOOB(
        @NonNull final UnprovisionedMeshNode unprovisionedMeshNode,
        @NonNull final InputOOBAction oobAction
    ) throws IllegalArgumentException;
```
Use the `MeshNetwork` object to edit Mesh Network properties such as Network name, Provisioners and
their properties (Name Address, TTL and Address Ranges), Network Keys, App Keys.

Following is an example on how to send a `GenericOnOffSet` message.
```java
    final GenericOnOffSet genericOnOffSet = new GenericOnOffSet(
        appKey, // App Key to sign the request with
        state,  // The new state
        new Random().nextInt()
    );
    mMeshManagerAPi.createMeshPdu(address, genericOnOffSet);
```
and Config messages can also be sent similarly.

## Author

Mobile Applications Team, Nordic Semiconductor ASA.

Contact: roshanrajaratnam <roshan.rajaratnam@nordicsemi.no>

## License

The Android-nRF-Mesh-Library is available under BSD 3-Clause license. See the LICENSE file for more info.


# Introduction

Qiscus Chat SDK (Software Development Kit) is a product provided by Qiscus that enables you to embed an in-app chat/chat feature in your applications quickly and easily. With our chat SDK, you can implement chat feature without dealing with the complexity of real-time communication infrastructure. We provide a powerful API to let you implement chat feature into your apps in the most seamless development process.

Qiscus Chat SDK provides many features such as:

* 1-on-1 chat
* Group chat
* Channel chat
* Typing indicator
* Image and file attachment
* Online presence
* Delivery receipt
* Read receipt
* Delete message
* Offline message
* Block user
* Custom real-time event
* Server-side integration with Server API and Webhook
* Embed bot engine in your app
* Enable push notification
* Export and import messages from your app

## Try Sample App

We have provided a sample app to help you get to know with our chat SDK. This sample app is built with full functionalities so that you can figure out the flow and main activities using Qiscus Chat SDK. Now you can freely customize your own UI. You can also build your own app on top of our sample app. For further details you can download this sample.

```
git clone https://github.com/qiscus/qiscus-chat-sdk-android-sample.git
```
This sample app uses sample APP ID, which means, by using this sample app, you will share the data with others. In case you want to try by your own, you can change the APP ID into your own APP ID. You can find your APP ID in your dashboard. Click [here](https://dashboard.qiscus.com/dashboard/login) to access your dashboard

# Getting Started

## **Step 1: Get Your APP ID**
First, you need to create your application in dashboard, by accessing [Qiscus Chat Dashboard](https://dashboard.qiscus.com/dashboard/login). You can create more than one App ID.

## **Step 2: Install Qiscus Chat SDK**
Qiscus Chat SDK requires minimum Android API 16 (Jelly Bean). To integrate your app with Qiscus, it can be done in 2 (two) steps. First, you need to add URL reference in your .gradle project. This reference is a guide for .gradle to get Qiscus Chat SDK from the right repository. Below is how to do that:

```
allprojects {
      repositories {
           ...
           maven { url "https://artifactory.qiscus.com/artifactory/qiscus-library-open-source" }
      }
}
```

Second, you need to add SDK dependencies inside your app .gradle. Then, you need to synchronize to compile the Qiscus Chat SDK for your app.

```
dependencies {
       ...
       implementation 'com.qiscus.sdk:chat-core:1.7.1'
}
```

## **Step 3: Initialization Qiscus Chat SDK**
You need to initiate your APP ID for your chat app before carry out to authentication. This initialization only needs to be done once in the app lifecycle. Initialization can be implemented in the initial startup. Here is how you can do that:

```java
public class SampleApp extends Application {

    @Override
    public void onCreate() {
        super.onCreate();

        QiscusCore.initWithAppId(this, APPID);

  }
 }
```
> The initialization should be called once across an Android app . The best practice you can put in application class.

## **Step 4: Authenticate to Qiscus**
To use Qiscus Chat SDK features, a user need to authenticate to Qiscus Server, for further detail you might figure out [Authentication section](https://documentation.qiscus.com/chat-sdk-android/authentications). This authentication is done by calling setUser() function. This function will retrieve or create user credential based on the unique userId, for example:

```java
QiscusCore.setUser(userId, userKey)
      .withUsername(username)
      .withAvatarUrl(avatarUrl)
      .withExtras(extras)
      .save(new QiscusCore.SetUserListener() {
          @Override
          public void onSuccess(QiscusAccount qiscusAccount) {
              //on success
          }
          @Override
          public void onError(Throwable throwable) {
              //on error
      });
```
Where:

**userId** (string, unique): A user identifier that will be used to identify a user and used whenever another user need to chat with this user. It can be anything, whether is is user's email, your user database index, etc. As long as it is unique and a string.

**userKey** (string): userKey for authentication purpose, so even if a stranger knows your user Id, he cannot access the user data.

**username** (string): Username for display name inside Chat Room purposes.

**avatarURL** (string, optional): to display user's avatar, fallback to default avatar if not provided.

You can learn from the figure below to understand what really happened when calling `setUser()` function:

### Updating User Profile

After your user account is created, sometimes you may need to update a user information, such as changing user avatar. You can use method `QiscusCore.updateUser()` to make changes to your account.

```java
QiscusCore.updateUser(userName, avatarUrl, new QiscusCore.SetUserListener() {
            @Override
            public void onSuccess(QiscusAccount qiscusAccount) {
                //do anything after it successfully updated
            }

            @Override
            public void onError(Throwable throwable) {
                //do anything if error occurs
            }
        });
```

### Clear User Data and disconnect

As mentioned in previous section, when you did setUser(), user's data will be stored locally. When user need to disconnect from Qiscus Chat SDK service, you need to clear the user data that is related to Qiscus Chat SDK, such as token, profile, messages, rooms, etc, from local device. You can do this by calling clearUser() method :

```java
QiscusCore.clearUser();
```

## Create Chat Room

**Chat Room** is a place where 2 or more users can chat each other. There are 3 type of Chat Room that can be created using Qiscus Chat SDK: 1-on-1 Chat Room, Group Chat Room, and Channel. For some cases, a room can be identified by room unique id or room name.

### 1-on-1 Chat Room

We assume that you already know a targeted user you want to chat with. Make sure that your targeted user has been registered in Qiscus Chat SDK through setUser() method, as explained in the previous section. To start a conversation with your targeted user, it can be done with `getChatRoom()` method. Qiscus Chat SDK, then, will serve you a new Chat Room, asynchronously. When the room is succesfully created, Qiscus Chat SDK will return a Chat Room package through `onSuccess()` listener.

```java
QiscusApi.getInstance().chatUser(userId, options)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(chatRoom -> {
                    // on success
                }, throwable -> {
                    // on error
                });
```

Where:

**userId**: A User identifier that will be used to identify a user and used whenever another user needs to chat with this user. It can be anything, whether is user's email, your user database index, etc. As long as it is unique and a string.

**distinctId**: (deprecated) you can fill “ ” (empty string).

**options**: metadata that can be as additional information to Chat Room, which consists key-value, for example, key: background, and value: red.

### Group Chat Room

When you want your many users to chat together in a 1-on-1 chat room, you need to create Group Room. Basically Group Room has the same concept as 1-on-1 Chat Room, but the different is that Group Room will target array of userIds in a single method. Here how you can create Group Room:

```java
QiscusApi.getInstance().createGroupChat(roomName, userIds, avatarUrl, options);
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(chatRoom -> {
                    // on success
                }, throwable -> {
                    // on error
                });
```

## Channel

Creating Channel Chat Room is ideal for a use case that requires a lot of number participants, more than 100.

You need to set uniqueId to identify a Channel Chat Room. If a Chat Room with predefined **uniqueId** doesn't exist, it will create a new one with requester as the only one participant. Otherwise, if Chat Room with predefined **uniqueId** already exists, it will return that room and add requester as a participant.

When the room doesn't exist at the very first call and you do not send avatarUrl and/or roomName, it will use the default value. But, after the second call (room does exist) and you send avatarUrl and/or roomName, it will be updated to that value.
For example the implementation creating channel:

```java
QiscusApi.getInstance().createChannel(uniqueId, roomName, avatarUrl, options)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(chatRoom -> {
                    // on success
                }, throwable -> {
                    // on error
                });
```

### Room Participant Management

In some cases, you may need to add additional participants into your room chat or even removing any participant.

### Get Chat Room List

To get all room list you can call QiscusApi.getInstance().getChatRooms(int page, int limit, boolean showMembers), page start from 1, limit indicate the max rooms per page, showMembers is flag for load room members also or not. Here sample code:

```java
QiscusApi.getInstance().getAllChatRooms(showParticipant, showRemoved, showEmpty, page, limit)
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(chatRooms -> {
        //on success
    }, throwable -> {
        //on error
    });
```

### Add Participant in Chat Room
You can add more than a participant in Chat Room by calling addRoomMember method. You can also pass multiple userIds. Once a participant succeeds to join the Chat Room, they will get a new Chat Room in their Chat Room list.

```java
QiscusApi.getInstance().addParticipants(roomId, userId)
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(chatRoom -> {
           // on success
        }, throwable -> {
            //on error
        });
```

### Remove Participant in Chat Room
You can remove more than a participant in Chat Room by calling removeRoomMember method. You can also pass multiple userId. Once a participant is removed from the Chat Room, they will not find related Chat Room in their Chat Room list.

```java
QiscusApi.getInstance().removeParticipants(roomId, userId)
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(chatRoom -> {
           //success
        }, throwable -> {
            //error
        });
```

## Enable Push Notification

First install FCM to your apps, you can follow [this steps.](https://firebase.google.com/docs/cloud-messaging/android/client) You can skip this step, if your apps already use FCM. Then put your api key to qiscus dashboard.
Now lets integrate with Qiscus client sdk, first enable FCM at Qiscus chat config.

```java
Qiscus.getChatConfig().setEnableFcmPushNotification(true);
```

After that, you need register FCM token to notify Qiscus Chat SDK, you can call sendCurrentToken() after login and in home page (was login in qiscus), for example:

```java
if (Qiscus.hasSetupUser()) {
    FirebaseUtil.sendCurrentToken();
}
```

```
public class FirebaseUtil {

    public static void sendCurrentToken() {
        AppFirebaseMessagingService.getCurrentDeviceToken();
    }
}
```

* Add the .AppFirebaseMessagingService in Manifest as well, for example:

```
<service android:name=".AppFirebaseMessagingService"
         android:exported="true">
    <intent-filter>
        <action android:name="com.google.firebase.MESSAGING_EVENT" />
    </intent-filter>
</service>
```

### Handle Incoming Message From Push Notification

After registering your FCM token, you will get data from FCM Qiscus Chat SDK, you can handle by  using `handleMessageReceived()` method, for example:

```

import android.util.Log;

import androidx.annotation.NonNull;
import com.google.firebase.messaging.FirebaseMessaging;
import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;
import com.qiscus.sdk.chat.core.QiscusCore;
import com.qiscus.sdk.chat.core.util.QiscusFirebaseMessagingUtil;

public class AppFirebaseMessagingService extends FirebaseMessagingService {

    @Override
    public void onMessageReceived(RemoteMessage remoteMessage) {
        super.onMessageReceived(remoteMessage);

        Log.d("Qiscus", "onMessageReceived " + remoteMessage.getData().toString());
        if (QiscusFirebaseMessagingUtil.handleMessageReceived(remoteMessage)) {
            return;
        }
    }

    @Override
    public void onNewToken(@NonNull String s) {
        super.onNewToken(s);

        Log.d("Qiscus", "onNewToken " + s);
        QiscusCore.registerDeviceToken(s);
    }

    public static void getCurrentDeviceToken() {
        final String token = QiscusCore.getFcmToken();
        if (token != null) {
            FirebaseMessaging.getInstance().deleteToken()
                    .addOnCompleteListener(task -> {
                        QiscusCore.removeDeviceToken(token);
                        getTokenFcm();
                    })
                    .addOnFailureListener(e -> QiscusCore.registerDeviceToken(token));
        } else {
            getTokenFcm();
        }
    }

    private static void getTokenFcm() {
        FirebaseMessaging.getInstance().getToken()
                .addOnCompleteListener(task -> {
                    if (task.isSuccessful() && task.getResult() != null) {
                        QiscusCore.registerDeviceToken(task.getResult());
                    } else {
                        Log.e("Qiscus", "getCurrentDeviceToken Failed : " +
                                task.getException());
                    }
                });
    }
}
```

## Event Handler

Qiscus Chat SDK provides a simple way to let applications publish and listen to some real-time events. You can publish typing, read, user status, custom event so that you can handle freely in the event handler. This lets you inform users that another participant is actively engaged in communicating with them.

Qiscus Chat SDK is using EventBus for broadcasting event to the entire applications. You can learn more about EventBus on this [website](http://greenrobot.org/eventbus/). What you need to do is register the object which will receive event from EventBus. You can call it like this:

```java
EventBus.getDefault().register(this);
```

Once you don't need to listen to the event anymore, you have to unregister the receiver by calling this method:

```java
EventBus.getDefault().unregister(this);
```

This is an example on how to register an activity to receive event from EventBus:

```java
public class MyActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my);

        // listen room event
        QiscusPusherApi.getInstance().subscribeChatRoom(qiscusChatRoom);

        // listen user status
        QiscusPusherApi.getInstance().subscribeUserOnlinePresence("userId");
    }

    @Override
    protected void onResume() {
        super.onResume();
        EventBus.getDefault().register(this); // register to EventBus
    }

    @Override
    protected void onPause() {
        super.onPause();
        EventBus.getDefault().unregister(this); // unregister from EventBus
    }

    @Subscribe
    public void onReceiveComment(QiscusCommentReceivedEvent event) {
        event.getQiscusComment(); // to get the comment
    }

    @Subscribe
    public void onReceiveRoomEvent(QiscusChatRoomEvent roomEvent) {
        switch (roomEvent.getEvent()) {
            case TYPING:
                roomEvent.getRoomId(); // this is the room id
                roomEvent.getUser(); // this is the qiscus user id
                roomEvent.isTyping(); // true if the user is typing
                break;
            case DELIVERED:
                roomEvent.getRoomId(); // this is the room id
                roomEvent.getUser(); // this is the qiscus user id
                roomEvent.getCommentId(); // the comment id was delivered
                break;
            case READ:
                roomEvent.getRoomId(); // this is the room id
                roomEvent.getUser(); // this is the qiscus user id
                roomEvent.getCommentId(); // the comment id was read
                break;
            case CUSTOM:
                //here, you can listen custom event
                roomEvent.getRoomId(); // this is the room id
                roomEvent.getUser(); // this is the qiscus user id
                roomEvent.getEventData(); //event data (JSON)
                break;
        }
    }

    @Subscribe
    public void onUserStatusChanged(QiscusUserStatusEvent event) {
        event.getUser(); // this is the qiscus user id
        event.isOnline(); // true if user is online
        event.getLastActive(); // Date of last active user
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        // stop listening room event
        QiscusPusherApi.getInstance().unsubsribeChatRoom(qiscusChatRoom);

        // stop listening user status
        QiscusPusherApi.getInstance().unsubscribeUserOnlinePresence("qiscus_user_id");
    }
}
```

## Using Proguard

ProGuard is the most popular optimizer for Java bytecode. It makes your Java and Android applications smaller and faster. Read [here](https://www.guardsquare.com/en/proguard) for more detail about Proguard.
If you are using Proguard in your application, make sure you add Proguard rules of Qiscus from [Qiscus Proguard Rules](https://github.com/qiscus/qiscus-sdk-android/blob/master/app/proguard-rules.pro) to your Proguard rules.

## RXJava Support

For you who prefer to code with RXJava, Qiscus Chat SDK does support RXJava. So, you can do anything as you do with Native Java. For example, to set a user, as has been explained in [Basic Authentication](#basic-authentication) section, you can do the same with RXJava. Here an example of how you can set user with it:

```java
// Setup qiscus account with rxjava example
Qiscus.setUser("user@email.com", "password")
      .withUsername("Tony Stark")
      .withAvatarUrl("http://avatar.url.com/handsome.jpg")
      .save()
      .subscribeOn(Schedulers.io())
      .observeOn(AndroidSchedulers.mainThread())
      .subscribe(qiscusAccount -> {
          //do anything if success
      }, throwable -> {
          //do anything if error occurs
      });
```


[![EO principles respected here](https://www.elegantobjects.org/badge.svg)](https://www.elegantobjects.org)
[![DevOps By Rultor.com](https://www.rultor.com/b/cqfn/jpeek)](https://www.rultor.com/p/cqfn/jpeek)
[![We recommend IntelliJ IDEA](https://www.elegantobjects.org/intellij-idea.svg)](https://www.jetbrains.com/idea/)

[![mvn](https://github.com/cqfn/jpeek/actions/workflows/mvn.yml/badge.svg)](https://github.com/cqfn/jpeek/actions/workflows/mvn.yml)
[![Javadoc](http://www.javadoc.io/badge/org.jpeek/jpeek.svg)](http://www.javadoc.io/doc/org.jpeek/jpeek)
[![PDD status](https://www.0pdd.com/svg?name=cqfn/jpeek)](https://www.0pdd.com/p?name=cqfn/jpeek)
[![Maven Central](https://img.shields.io/maven-central/v/org.jpeek/jpeek.svg)](https://maven-badges.herokuapp.com/maven-central/org.jpeek/jpeek)
[![License](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/cqfn/jpeek/blob/master/LICENSE.txt)
[![codecov](https://codecov.io/gh/cqfn/jpeek/branch/master/graph/badge.svg)](https://codecov.io/gh/cqfn/jpeek)
[![jpeek report](https://i.jpeek.org/org.jpeek/jpeek/badge.svg)](https://i.jpeek.org/org.jpeek/jpeek/)
[![SonarQube](https://img.shields.io/badge/sonar-ok-green.svg)](https://sonarcloud.io/dashboard?id=org.jpeek%3Ajpeek)
[![Hits-of-Code](https://hitsofcode.com/github/cqfn/jpeek)](https://hitsofcode.com/view/github/cqfn/jpeek)

jPeek is a static collector of Java code metrics.

**Motivation**:
[Class cohesion](http://www.jot.fm/issues/issue_2008_07/article1.pdf), for example,
is considered as one of most important object-oriented software attributes.
There are
[over 30](http://www.math.md/files/csjm/v25-n1/v25-n1-(pp44-74).pdf)
different cohesion metrics invented so far, but almost none of them
have calculators available. The situation with other metrics is very similar.
We want to create such a tool that will make it
possible to analyze code quality more or less formally (with hundreds of metrics). Then, we will
apply this analysis to different Java libraries with an intent to prove
that the ideas from [Elegant Objects](https://www.yegor256.com/elegant-objects.html)
book series make sense.

## How to use?

Load the latest `jar-with-dependencies.jar` file from
[here](https://repo1.maven.org/maven2/org/jpeek/jpeek/)
and then:

```bash
$ java -jar jpeek-jar-with-dependencies.jar --sources . --target ./jpeek
```

jPeek will analyze Java files in the current directory.
XML reports will be generated in the `./jpeek` directory. Enjoy.

Available CLI options

| Option                      | Description                                                                        |
|-----------------------------|------------------------------------------------------------------------------------|
| `-s, --sources <path>`      | **Required.** Path to directory with the class files                               |
| `-t, --target <path>`       | **Required.** Path to directory where the reports will be generated                |
| `--include-ctors`           | Include constructors into all formulas                                             |
| `--include-static-methods`  | Include static methods into all formulas                                           |
| `--include-private-methods` | Include private methods into all formulas                                          |
| `--metrics <metrics>`       | Comma-separated list of metrics to include (default: `"LCOM5,NHD,MMAC,SCOM,CAMC"`) |
| `--overwrite`               | Overwrite the target directory, if it exists, or exit with error                   |
| `--quiet`                   | Turn off logging                                                                   |
| `--help`                    | Display help message                                                               |

You can also deploy it as a web service to your own platform. Just compile it
with `mvn clean package --settings settings.xml` and then run, as `Procfile` suggests.
You will need to have `settings.xml` with the following data:

```xml
<settings>
  <profiles>
    <profile>
      <id>jpeek-heroku</id>
      <activation>
        <activeByDefault>true</activeByDefault>
      </activation>
      <properties>
        <sentry.dsn>https://...</sentry.dsn>
        <dynamo.key>AKIAI..........LNN6A</dynamo.key>
        <dynamo.secret>6560KMv5+8Ti....................Qdwob63Z</dynamo.secret>
      </properties>
    </profile>
  </profiles>
</settings>
```

You will also need these tables in DynamoDB (all indexes must deliver `ALL` attributes):

```
jpeek-mistakes:
  metric (HASH/String)
  version (RANGE/String)
  indexes:
    mistakes (GSI):
      version (HASH/String),
      avg (RANGE/Number)
jpeek-results:
  artifact (HASH/String)
  indexes:
    ranks (GSI):
      version (HASH/String)
      rank (RANGE/Number)
    scores (GSI):
      version (HASH/String)
      score (RANGE/Number)
    recent (GSI):
      good (HASH/String)
      added (RANGE/Number)
```

## Cohesion Metrics

These papers provide a pretty good summary of cohesion metrics:

[`izadkhah17`]
Habib Izadkhah et al.,

_Class Cohesion Metrics for Software Engineering: A Critical Review_,

Computer Science Journal of Moldova, vol.25, no.1(73), 2017,
[PDF](http://www.math.md/files/csjm/v25-n1/v25-n1-(pp44-74).pdf).

[`badri08`]
Linda Badri et al.,

_Revisiting Class Cohesion: An empirical investigation on several systems_,

Journal of Object Technology, vol.7, no.6, 2008,
[PDF](http://www.jot.fm/issues/issue_2008_07/article1.pdf).

### Here is a list of metrics we have already implemented (in order or their appearance):

[`chidamber94`]
Lack of Cohesion in Methods (**LCOM**).

Shyam Chidamber et al.,

_A metrics suite for object oriented design_,

IEEE Transactions on Software Engineering, vol.20, no.6, 1994,
[PDF](papers/chidamber94_LCOM.pdf).

[`bieman95`]
Tight Class Cohesion (**TCC**) and Loose Class Cohesion (**LCC**).

James M. Bieman et al.,

_Cohesion and Reuse in an Object-Oriented System_,

Department of Computer Science, Colorado State University, 1995,
[PDF](papers/bieman95_TCC.pdf).

[`hitz95`]
Lack of Cohesion in Methods 4 (**LCOM4**).

Martin Hitz et al.,

_Measuring Coupling and Cohesion In Object-Oriented Systems_,

Institute of Applied Computer Science and Systems Analysis, University of Vienna, 1995,
[PDF](papers/hitz95_LCOM4.pdf).

[`sellers96`]
Lack of Cohesion in Methods 2-3 (**LCOM 2, 3 and 5**).

B. Henderson-Sellers et al.,

_Coupling and cohesion (towards a valid metrics suite for object-oriented analysis and design)_,

Object Oriented Systems 3, 1996,
[PDF](papers/sellers96_LCOM2_LCOM3_LCOM5.pdf).

[`bansiya99`]
Cohesion Among Methods of Classes (**CAMC**).

Jagdish Bansiya et al.,

_A class cohesion metric for object-oriented designs_,

Journal of Object-Oriented Programming, vol. 11, no. 8, 1999,
[PDF](papers/bansiya99_CAMC.pdf).

[`etzkorn00`]
LOgical Relatedness of Methods (**LORM**).

L. Etzkorn and H. Delugach,

_Towards a semantic metrics suite for object-oriented design_,

Technology of Object-Oriented Languages and Systems, 2000. TOOLS 34. Proceedings. 34th International Conference on. IEEE, 2000, pp. 71–80,
[PDF](papers/etzkorn00_LORM.pdf)

[`wasiq01`]
Class Connection Metric (**CCM**).

M. Wasiq

_Measuring Class Cohesion in Object-Oriented Systems_,

Master Thesis at the King Fahd University of Petroleum & Minerals, 2001,
[PDF](papers/wasiq01_CCM.pdf).

[`aman04`]
Optimistic Class Cohesion (**OCC**) and Pessimistic Class Cohesion (**PCC**).

Hirohisa Aman et al.,

_A proposal of class cohesion metrics using sizes of cohesive parts_,

Proc. of Fifth Joint Conference on Knowledge-based Software Engineering, 2002,
[PDF](papers/aman04_OCC_PCC.pdf).

[`marcus05`]
Conceptual Cohesion of Classes (**C3**).

A. Marcus and D. Poshyvanyk,

_The conceptual cohesion of classes_,

21st IEEE International Conference on Software Maintenance (ICSM'05), Budapest, Hungary, 2005, pp. 133-142,
[PDF](papers/marcus05_C3.pdf)

[`counsell06`]
Normalized Hamming Distance (**NHD**).

Steve Counsell et al.,

_The interpretation and utility of three cohesion metrics for object-oriented design_,

ACM TOSEM, April 2006,
[PDF](papers/counsell06_NHD.pdf).

[`fernandez06`]
A Sensitive Metric of Class Cohesion (**SCOM**).

Luis Fernández et al.,

_[A] new metric [...] yielding meaningful values [...] more sensitive than those previously reported_,

International Journal "Information Theories & Applications", Volume 13, 2006,
[PDF](papers/fernandez06_SCOM.pdf).

[`dallal07`]
Method-Method through Attributes Cohesion (**MMAC**).

Jehad Al Dallal,

_A Design-Based Cohesion Metric for Object-Oriented Classes_,

World Academy of Science, Engineering and Technology International Journal of Computer and Information Engineering Vol:1, No:10, 2007,
[PDF](papers/dallal07_MMAC.pdf).

[`liu09`]
Maximal Weighted Entropy (**MWE**).

Y. Liu, D. Poshyvanyk, R. Ferenc, T. Gyim´othy, and N. Chrisochoides,

_Modeling class cohesion as mixtures of latent topics_,

Software Maintenance, 2009. ICSM 2009. IEEE International Conference on. IEEE, 2009, pp. 233–242,
[PDF](papers/liu09_MWE.pdf)

[`dallal11`]
Transitive Lack of Cohesion in Methods (**TLCOM**).

Jehad Al Dallal,

_Transitive-based object-oriented lack-of-cohesion metric_,

Department of Information Science, Kuwait University, 2011,
[PDF](papers/dallal11_TLCOM.pdf).

## How it works?

First, `Skeleton` parses Java bytecode using Javaassit and ASM, in order to produce
`skeleton.xml`. This XML document contains information about each class, which
is necessary for the metrics calculations. For example, this simple Java
class:

```java
class Book {
  private int id;
  int getId() {
    return this.id;
  }
}
```

Will look like this in the `skeleton.xml`:

```xml
<class id='Book'>
  <attributes>
   <attribute public='false' static='false' type='I'>id</attribute>
  </attributes>
  <methods>
    <method abstract='false' ctor='true' desc='()I' name='getId' public='true' static='false'>
      <return>I</return>
      <args/>
    </method>
  </methods>
</class>
```

Then, we have a collection of XSL stylesheets, one per each metric. For example,
`LCOM.xsl` transforms `skeleton.xml` into `LCOM.xml`, which may look like this:

```xml
<metric>
  <title>MMAC</title>
  <app>
    <class id='InstantiatorProvider' value='1'/>
    <class id='InstantationException' value='0'/>
    <class id='AnswersValidator' value='0.0583'/>
    <class id='ClassNode' value='0.25'/>
    [... skipped ...]
  </app>
</metric>
```

Thus, all calculations happen inside the XSLT files. We decided to implement
it this way after a less successful attempt to do it all in Java. It seems
that XSL is much more suitable for manipulations with data than Java.

### jPeek maven plugin
We are developing a jPeek plugin for Maven, see [jPeek Maven plugin](https://github.com/yegor256/jpeek-maven-plugin) project.

## Known Limitations

* The java compiler is known to inline constant variables as per [JLS 13.1](https://docs.oracle.com/javase/specs/jls/se8/html/jls-13.html#jls-13.1). This affects the results calculated by metrics that take into account access to class attributes if these are `final` constants. For instance, all LCOM and COM metrics are affected.

## How to contribute?

Just fork, make changes, run `mvn clean install -Pqulice` and submit
a pull request; read [this](http://www.yegor256.com/2014/04/15/github-guidelines.html),
if lost.

## Contributors

  - [@yegor256](https://github.com/yegor256) as Yegor Bugayenko ([Blog](https://www.yegor256.com))
  - [@alayor](https://github.com/alayor) as Alonso A. Ortega ([Blog](http://www.alayor.com))
  - [@memoyil](https://github.com/memoyil) as Mehmet Yildirim
  - [@sergey-karazhenets](https://github.com/sergey-karazhenets) as Sergey Karazhenets
  - [@llorllale](https://github.com/llorllale) as George Aristy
  - [@mesut](https://github.com/mesut) as Mesut Özen
  - [@serranya](https://github.com/serranya) as Peter Lamby
  - [@humb1t](https://github.com/humb1t) as Nikita Puzankov
  - [@stepanov-dmitry](https://github.com/stepanov-dmitry) as Dmitry Stepanov
  - [@GnusinPavel](https://github.com/GnusinPavel) as Gnusin Pavel
  - [@mohamednizar](https://github.com/mohamednizar) as Mohamed Nizar

Don't hesitate to add your name to this list in your next pull request.


## Important notice

As of Minecraft `1.20.1` this version of Building Gadget (Building Gadgets 1) is officially the 'Legacy' version of Building Gadgets. I will continue to update the mod for the Minecraft versions we already support but this mod will not be ported to Minecraft `1.20.1` or higher.

Go and download [`Building Gadgets 2`](https://github.com/direwolf20-mc/buildinggadgets2)! It's the same mod but with a bunch of new and awesome features built on a brand-new codebase.

---

Building Gadgets (1.12.2 - 1.19.4)

Update! See Building Gadgets 2 for Minecraft 1.20.1 and higher!

Sometimes, building large structures can be a little tedious, and take a lot of effort. Not all of us are great builders you know!

# Welcome to Dire's Building Gadgets!

Dire's `Building Gadgets` aims to make building a little bit easier. At this time there are `four` tools, and they are described below. Alternatively, watch the following mod spotlight for instructions!

[![Youtube Thumbnail](https://img.youtube.com/vi/D4Ib4h7aTSk/maxresdefault.jpg)](https://www.youtube.com/watch?v=D4Ib4h7aTSk)
_(I'm a Youtube Link, click me)_

## Usage

To see how to use Dire's `Building Gadgets` refer to our [Wiki](https://github.com/Direwolf20-MC/BuildingGadgets/wiki) which has been recently updated to be inline with the mod as of `1.16.5`. Please feel free to make feature requests to add more info to our wiki. We're always trying to improve.

Direwolf20's Spotlights - [Spotlight Part One](https://youtu.be/D4Ib4h7aTSk) and [Spotlight Part Two](https://youtu.be/JS1Xx_kwQQ0)

### Using the Gadgets

Building gadgets has grown a lot since its initial release to the point that we have multiple tools for every occasion, Our wiki has a great set of instructions on how to use each of our Gadgets, check out the

- ![](./src/main/resources/assets/buildinggadgets/textures/item/gadget_building.png) [Building Gadget](https://github.com/Direwolf20-MC/BuildingGadgets/wiki/Building-Gadget)
- ![](./src/main/resources/assets/buildinggadgets/textures/item/gadget_exchanging.png) [Exchanger Gadget](https://github.com/Direwolf20-MC/BuildingGadgets/wiki/Exchanger-Gadget)
- ![](./src/main/resources/assets/buildinggadgets/textures/item/gadget_copy_paste.png) [Copy Paste Gadget](https://github.com/Direwolf20-MC/BuildingGadgets/wiki/Copy-Paste-Gadget)
- ![](./src/main/resources/assets/buildinggadgets/textures/item/gadget_destruction.png) [Destruction Gadget](https://github.com/Direwolf20-MC/BuildingGadgets/wiki/Destruction-Gadget)

## Schematics / Templates

Building Gadgets' copy & paste tool supports loading and saving templates via the `Template Manager` block. You can use this feature to save your builds and share them with others.

If you're looking for templates to use, check out the [r/9x9](https://www.reddit.com/r/9x9/) community on reddit 🎉

## Contributing

See [Important Notice](#important-notice). Please support the new version of Building Gadgets at https://github.com/direwolf20-mc/buildinggadgets2 if you would like to support the project! You can see help maintain the legacy version by submitting PR's to this repo.

## Credit

An especially big thanks to all of our [contributors](https://github.com/Direwolf20-MC/BuildingGadgets/graphs/contributors) for the continued support, bug reports (issues) and PR's. All of the [Forge Guys](https://github.com/orgs/MinecraftForge/people) and the creators of Minecraft (*Duh*)

[License MIT](License.md)


Microsoft Authentication Library (MSAL) for Android
==============================================

|[📚Documentation](https://learn.microsoft.com/en-us/azure/active-directory/develop/tutorial-v2-android) | [ 🚀 Getting Started](https://learn.microsoft.com/en-us/azure/active-directory/develop/quickstart-mobile-app-android-sign-in) | [💻 Sample Code](https://github.com/AzureAD/microsoft-authentication-library-for-android/wiki/MSAL-Code-Samples)| [ 📖 Library Reference](http://javadoc.io/doc/com.microsoft.identity.client/msal) | [🛠 Support](README.md#community-help-and-support) | [🌐 Docs Site](https://learn.microsoft.com/en-us/azure/active-directory/develop/scenario-mobile-overview)
| --- | --- | --- | --- | --- | --- |

## Introduction
The Microsoft Authentication Library (MSAL) for Android enables developers to acquire security tokens from the Microsoft identity platform to authenticate users and access secured web APIs for their Android based applications.
The MSAL library for Android gives your app the ability to use the [Microsoft Cloud](https://cloud.microsoft.com) by supporting [Microsoft Azure Active Directory](https://azure.microsoft.com/services/active-directory/) and [Microsoft Personal Accounts](https://account.microsoft.com)  using industry standard OAuth2 and OpenID Connect. The library also supports [Azure AD B2C](https://azure.microsoft.com/services/active-directory-b2c/).

[![Version Badge](https://img.shields.io/maven-central/v/com.microsoft.identity.client/msal.svg)](https://repo1.maven.org/maven2/com/microsoft/identity/client/msal/)

### :exclamation: Migrating from ADAL

ADAL Android was deprecated on June 2023. We do not support ADAL. See the [ADAL to MSAL migration guide for Android](https://docs.microsoft.com/azure/active-directory/develop/migrate-android-adal-msal)

## Using MSAL

- Before you can get a token from Azure AD v2.0 or Azure AD B2C, you'll need to register an application. To register your app, use [the Azure portal](https://aka.ms/AppRegistrations). For Azure AD B2C, checkout [how to register your app with B2C](https://docs.microsoft.com/azure/active-directory-b2c/active-directory-b2c-app-registration).

### Requirements

- Min SDK Version 16+
- Target SDK Version 33+

### Step 1: Declare dependency on MSAL

Add to your app's build.gradle:

```gradle
dependencies {
    implementation 'com.microsoft.identity.client:msal:4.9.+'
}
```

Please also add the following lines to your repositories section in your gradle script:

```gradle
maven {
    url 'https://pkgs.dev.azure.com/MicrosoftDeviceSDK/DuoSDK-Public/_packaging/Duo-SDK-Feed/maven/v1'
}
```

### Step 2: Create your MSAL configuration file

[Configuration Documentation](https://docs.microsoft.com/azure/active-directory/develop/msal-configuration)

It's simplest to create your configuration file as a "raw" resource file in your project resources.  You'll be able to refer to this using the generated resource identifier when constructing an instance of PublicClientApplication. If you are registering your app in the portal for the first time, you will also be provided with this config JSON.

```javascript
{
  "client_id" : "<YOUR_CLIENT_ID>",
  "redirect_uri" : "msauth://<YOUR_PACKAGE_NAME>/<YOUR_BASE64_URL_ENCODED_PACKAGE_SIGNATURE>",
  "broker_redirect_uri_registered": true,
}
```

>NOTE: In the `redirect_uri`, the part `<YOUR_PACKAGE_NAME>` refers to the package name returned by the `context.getPackageName()` method. This package name is the same as the [`application_id`](https://developer.android.com/studio/build/application-id) defined in your `build.gradle` file.

>NOTE: This is the minimum required configuration.  MSAL relies on the defaults that ship with the library for all other settings.  Please refer to the [configuration file documentation](https://docs.microsoft.com/azure/active-directory/develop/msal-configuration) to understand the library defaults.

### Step 3: Configure the AndroidManifest.xml

1. Request the following permissions via the Android Manifest

```XML
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
```

2. Configure an intent filter in the Android Manifest, using your redirect URI

>NOTE: Failure to include an intent filter matching the redirect URI you specify via configuration will result in a failed interactive token request.  Please double check this!

```XML
    <!--Intent filter to capture authorization code response from the default browser on the device calling back to our app after interactive sign in -->
    <activity
        android:name="com.microsoft.identity.client.BrowserTabActivity">
        <intent-filter>
            <action android:name="android.intent.action.VIEW" />
            <category android:name="android.intent.category.DEFAULT" />
            <category android:name="android.intent.category.BROWSABLE" />
            <data
                android:scheme="msauth"
                android:host="<YOUR_PACKAGE_NAME>"
                android:path="/<YOUR_BASE64_ENCODED_PACKAGE_SIGNATURE>" />
        </intent-filter>
    </activity>
```

>NOTE: Please refer to [this FAQ](https://github.com/AzureAD/microsoft-authentication-library-for-android/wiki/MSAL-FAQ#redirect-uri-issues) for more information on common redirect uri issues.

### Step 4: Create an MSAL PublicClientApplication

>NOTE: In this example we are creating an instance of MultipleAccountPublicClientApplication, which is designed to work with apps that allow multiple accounts to be used within the same application. If you would like to use SingleAccount mode, refer to the [single vs. multi account documentation](https://docs.microsoft.com/azure/active-directory/develop/single-multi-account). You can also check out the [quickstart](https://docs.microsoft.com/azure/active-directory/develop/quickstart-v2-android) for examples of how this is used.

1.  Create a new MultipleAccountPublicClientApplication instance.

```Java

String[] scopes = {"User.Read"};
IMultipleAccountPublicClientApplication mMultipleAccountApp = null;
IAccount mFirstAccount = null;

PublicClientApplication.createMultipleAccountPublicClientApplication(getContext(),
    R.raw.msal_config,
    new IPublicClientApplication.IMultipleAccountApplicationCreatedListener() {
        @Override
        public void onCreated(IMultipleAccountPublicClientApplication application) {
            mMultipleAccountApp = application;
        }

        @Override
        public void onError(MsalException exception) {
            //Log Exception Here
        }
    });
```

2. Acquire a token interactively

```java

mMultipleAccountApp.acquireToken(this, SCOPES, getAuthInteractiveCallback());

private AuthenticationCallback getAuthInteractiveCallback() {
    return new AuthenticationCallback() {
        @Override
        public void onSuccess(IAuthenticationResult authenticationResult) {
            /* Successfully got a token, use it to call a protected resource */
            String accessToken = authenticationResult.getAccessToken();
            // Record account used to acquire token
            mFirstAccount = authenticationResult.getAccount();
        }
        @Override
        public void onError(MsalException exception) {
            if (exception instanceof MsalClientException) {
                //And exception from the client (MSAL)
            } else if (exception instanceof MsalServiceException) {
                //An exception from the server
            }
        }
        @Override
        public void onCancel() {
            /* User canceled the authentication */
        }
    };
}
```

3. Acquire a token silently

```java

/*
    Before getting a token silently for the account used to previously acquire a token interactively, we recommend that you verify that the account is still present in the local cache or on the device in case of brokered auth

    Let's use the synchronous methods here which can only be invoked from a Worker thread
*/

//On a worker thread
IAccount account = mMultipleAccountApp.getAccount(mFirstAccount.getId());

if(account != null){
    //Now that we know the account is still present in the local cache or not the device (broker authentication)

    //Request token silently
    String[] newScopes = {"Calendars.Read"};

    String authority = mMultipleAccountApp.getConfiguration().getDefaultAuthority().getAuthorityURL().toString();

    //Use default authority to request token from pass null
    IAuthenticationResult result = mMultipleAccountApp.acquireTokenSilent(newScopes, account, authority);
}

```

## ProGuard
MSAL uses reflection and generic type information stored in `.class` files at runtime to support various persistence and serialization related functionalities. Accordingly, library support for minification and obfuscation is limited. A default configuration is shipped with this library; please [file an issue](https://github.com/AzureAD/microsoft-authentication-library-for-android/issues/new/choose) if you find any issues.

## Community Help and Support

We use [StackOverflow](http://stackoverflow.com/questions/tagged/msal) with the community to provide support. You should browse existing issues to see if someone has asked about your issue before. If there are workable solutions to your issue then try out those solutions. If not, ask your question and let the community help you out. We're part of the community too and watch for new questions. We help with answers when the community cannot give you a solution.

If you find and bug or have a feature request, please raise the issue on [GitHub Issues](../../issues).

## Submit Feedback
We'd like your thoughts on this library. Please complete [this short survey](https://forms.office.com/r/3J8pAAqAcj).

## Contribute

We enthusiastically welcome contributions and feedback. You should [clone the repo and start contributing now](https://github.com/AzureAD/microsoft-authentication-library-for-android/wiki/MSAL-Contributing).

This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.

## Android Studio Build Requirement
Please note that this project uses [Lombok](https://projectlombok.org/) internally and while using Android Studio you will need to install [Lombmok Plugin](https://plugins.jetbrains.com/plugin/6317-lombok) to get the project to build successfully within Android Studio.

## Recommendation

MSAL is a security library. It controls how users sign-in and access services. We recommend you always take the latest version of our library in your app when you can. We use [semantic versioning](http://semver.org) so you can control the risk of updating your app. For example, always downloading the latest minor version number (e.g. x.*y*.x) ensures you get the latest security and feature enhanements with the assurance that our API surface area has not changed. You can always see the latest version and release notes under the [Releases](https://github.com/AzureAD/microsoft-authentication-library-for-android/releases) tab of GitHub.

## Security Reporting

If you find a security issue with our libraries or services, please report the issue to [secure@microsoft.com](mailto:secure@microsoft.com) with as much detail as you can provide. Your submission may be eligible for a bounty through the [Microsoft Bounty](http://aka.ms/bugbounty) program. Please do not post security issues to [GitHub Issues](https://github.com/AzureAD/microsoft-authentication-library-for-android/issues) or any other public site. We will contact you shortly after receiving your issue report. We encourage you to get new security incident notifications by visiting [Microsoft technical security notifications](https://technet.microsoft.com/en-us/security/dd252948) to subscribe to Security Advisory Alerts.

Copyright (c) Microsoft Corporation.  All rights reserved. Licensed under the MIT License (the "License");


Confluent Commons
=================

Contains 3 libraries: [metrics](metrics), [config](config), [utils](utils).

# Development
[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fconfluentinc%2Fcommon.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fconfluentinc%2Fcommon?ref=badge_shield)

## Overview

Typically you will run standard maven commands such as `mvn install` from the top-level directory (i.e. the directory
that contains this `README` file).  The sections below explain how to build specific sub-projects independently.

## Metrics

To build the `metrics` sub-project independently, run this from the parent directory:

    $ mvn -pl :common-metrics package

## Config

To build the `config` sub-project independently, run this from the parent directory:

    $ mvn -pl :common-config package

## Utils

To build the `utils` sub-project independently, run this from the parent directory:

    $ mvn -pl :common-utils package

## License
[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fconfluentinc%2Fcommon.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Fconfluentinc%2Fcommon?ref=badge_large)