jayeson.lib.streamfinder
v1.0.0
Published
Javascript implementation of Streamfinder client
Downloads
2
Readme
Streamfinder Library
The streamfinder library wraps around the HTTP calls of the streamfinder server and provides an API to the service. The library consists of 2 independent clients. User can choose to install the java client or the js client that runs on the browser.
Java Client
The java streamfinder module comes with a Discoverer, Advertiser, SessionFactory (for authentication and ticketing). An additional user permissions module can perform database queries for a list of known users and resolve stream permissions.
Installation
For java clients, include the following file in your build.gradle:
projDependency ':jayeson.lib.streamfinder', 'jayeson:jayeson.lib.streamfinder:1.0.0', false
Clients using only discover/advertise/authentication services will want to install the streamfinder module via Guice
import jayeson.lib.streamfinder.StreamfinderModule;
public class MyModule extends AbstractModule {
@Override
protected void configure() {
install(new StreamfinderModule());
}
}
Installing the users permission module requires a Guice setup...
import jayeson.lib.streamfinder.UserPermissionModule;
...
// In the overwritten AbstractModule.configure
install(new UserPermissionModule());
...and a copy of conf-sample/ to the conf/ folder of your executable project. Be sure to include the correct database URL/username/password in persistence.xml.
SessionFactory and Authentication
The SessionFactory is a singleton object that is used for authentication and ticketing purposes. After installing the streamfinder module with Guice, users can obtain a sessionFactory by injection.
@Inject
public void setSessionFactory(SessionFactory factory) {
// Store factory reference
}
At any time, SessionFactory stores a single session for a given user. This session is the same session that will be shared between discoverer and advertiser. We can access a session directly as such:
CompletableFuture<SessionToken> f = factory.getSession("username", "password", "http://streamfinder");
This call will login the user and create a session if none exist. If the user is already authenticated on the current application, the existing session will be retrieved and no additional HTTP calls will be made to the streamfinder server.
After the future completes and a SessionToken is available, we can access the sessionData.
SessionToken token = f.get();
String sessionData = token.getCookie();
This session data is a cookie header. It can be forwarded when manually making calls to protected resources which require user authentication.
When authentication fails due to a wrong username or password, users can check the session token for an error message.
if (!token.isAuthenticated()) {
System.out.println(token.getError());
}
Network errors that occur while creating a session are handled differently. These errors are retrieved from the future when it completes exceptionally. The session token will not be available when a network error occurs.
f.exceptionally(error -> {
try {
throw error.getCause();
} catch (UnknownHostException err) {
System.out.println("Unable to resolve address");
} catch (ConnectionException err) {
System.out.println("Cannot reach streamfinder server");
}
});
Users are advised not to access session tokens directly if possible. When using services such as discover/advertise, the tokens are internally managed. These session tokens are kept alive and automatically recreated if they expire. However, When accessing session tokens directly, users are exposed to these details and will have to manually maintain them. These tokens are said to be unmanaged.
Valid session tokens that are retrieved will be cached in the factory for later use. These tokens need to be regularly maintained by making calls with the token. However, if the same user session is shared by a discoverer or advertiser, the session does not have to be renewed as these services will make calls to keep the session alive.
If an unmanaged session token were to expire, users have to invalidate this token to prevent it from being resued in subsequent calls.
factory.invalidate("username", "password", "http://streamfinder");
After the session for the user is invalidated, subsequent calls to getSession() will return a new session.
Retrieving and Updating of Tickets
After performing authentication, users can generate tickets in the current session by using the SessionFactory.
factory.makeTicket("username", "password", "http://streamfinder", "clientId");
makeTicket will retrieve the session and create a new ticket in it.
On network errors, the future completes exceptionally with UnknownHostException or ConnectionException. If authentication fails or the session has expired, the future completes exceptionally with AuthenticationException.
Discovering Streams
To discover a list of available streams, users first need to create a Discoverer object. This discoverer object can be created by injecting a discoverer factory and calling the create function.
@Inject
public void setDiscovererFactory(DiscovererFactory factory) {
Discoverer discoverer = factory.create("username", "password", "http://streamfinder");
}
Once we have a discoverer object, we can begin to populate it with the query. In this example, the discoverer search for apple and coco.* streams in the fruits (group id 1) group. We query for tcp streams in this java client.
discoverer.discover((byte) 1, "apple").discover((byte) 1, "coco.*").with("tcp");
Retrieve the discovered list of streams and its sources by doing
List<Source> sources = discoverer.getSources();
The sources structure returned is a full snapshot of all available streams. sources returned from here contain a list of permitted streams and the connection URL for each stream.
Users can also attach listeners to get the latest streams when they are updated
discoverer.onUpdate(sources -> {
System.out.println(sources);
});
The update event is fired when there is a change in stream sources. The sources structure returned by the listener is a full snapshot of all available streams. This structure is similar to the getter method. Note that the callback is executed on the execution context of the streamfinder module.
Users should also setup a listener for error events.
discoverer.onError(error -> {
error.printStackTrace();
});
On network errors, an UnknownHostException or ConnectionException event is fired. If authentication fails or the session expires, an AuthenticationException is emitted.
Advertising Streams
First, create an Advertiser using an injected advertiser factory.
@Inject
public void setAdvertiserFactory(AdvertiserFactory factory) {
Advertiser advertiser = factory.create("username", "password", "http://streamfinder");
}
Do not run multiple advertisers with the same user name. This will corrupt the advertisements and cause flickering on the streamfinder server.
Once we have a advertiser, we can begin to populate it with advertisements. In this example, the advertiser publishes an 'apple' and 'orange' stream in the ‘fruits’ group (group id 1) with a level of 0. Both stream are published from tcp://123.123.123.123:9000.
advertiser.advertise((byte) 1, "apple", 0).advertise((byte) 1, "orange", 0).from("tcp://123.123.123.123:9000");
A single advertiser represents a datafront server. As such, all streams published under a single advertiser will be published as available from all connections registered. We can publish streams coming from different connections by separating them into different advertisers. This example shows how 'apple' and 'orange' streams can be advertised to be coming from 2 separate ports.
advertiser1.advertise((byte) 1, "apple", 0).from("tcp://123.123.123.123:9000");
advertiser2.advertise((byte) 1, "orange", 0).from("http://123.123.123.123:9001");
We can attach listeners to handle update events from the advertiser. These events are fired at regular intervals when the adtvertisement is sent to the server.
discoverer.onUpdate(() -> {
System.out.println("Advert sent to server");
});
Users should also setup a listener for error events.
discoverer.onError(error -> {
error.printStackTrace();
});
On network errors, an UnknownHostException or ConnectionException event is fired. If authentication fails or the session expires, an AuthenticationException is emitted.
User Permissions
The java streamfinder includes a module to resolve user access permissions. This module will connect to the database and retrieve users with their permission level. Clients can then supply a given stream and check if the user has permission to access it. The permission module can also resolve other parameters such as best level for a user.
Clients first have to retrieve a User object. Users can be queried from the UserRepository singleton by name.
@Inject
public void setUserRepo(UserRepository repo) {
User user = repo.findFeedUserByName("username");
}
The User object supports various operations. canAccess verifies if the user can access a particular stream.
boolean isAppleAllowed = user.canAccess((byte) 1, "apple");
levelAccessTo finds the best stream level that a user can connect to.
int bestAppleLevel = user.levelAccessTo((byte) 1, "apple");
getConfigIdFor finds the config id that is used to reference per-user configurations.
long configId = user.getConfigIdFor((byte) 1, "apple");
Javascript Client
The js streamfinder library consists of a Discoverer and a SessionFactory.
User authentication is not provided in js SessionFactory. Unlike its java counterpart, the js SessionFactory performs only ticketing calls. Client will have to perform the necessary authentication calls and retrieve authenticated session cookies before using streamfinder js. Failure to authenticate the session will result in errors emitted from the Discoverer and SessionFactory.
Finally, the UserPermissionModule in the java client is not available in the js library.
Installation
Js clients will have to reference the following files in the script tag:
<script src="http://code.jquery.com/jquery-2.1.4.min.js"></script>
<script src="http://underscorejs.org/underscore-min.js"></script>
<script src="https://cloud.github.com/downloads/Olical/EventEmitter/EventEmitter-4.0.3.min.js"></script>
<script src="https://gitlab.jayeson.com.sg/feed/jayeson.lib.streamfinder/raw/master/src/main/js/jayeson/discovery/js/discovery.js"></script>
Retrieving and Updating of Tickets
After performing authentication, users can generate tickets in the current session by using the SessionFactory.
SessionFactory.makeTicket("http://streamfinder", "clientId", function(ticket, error) {
if (ticket.length !== 0) {
console.log("Ticket for clientId: " + ticket);
} else if (error.length !== 0) {
console.log("Unable to retrieve ticket for clientId: " + error);
}
});
The result of the ticketing call is deposited in the callback function once the operation completes. A successful ticketing call will return a non-empty ticket string. However, if there were errors, the ticket string will be empty and the error param will contain the reason why ticketing has failed.
Discovering Streams
To discover a list of available streams, users first need to create a Discoverer object by supplying the URL of the streamfinder server.
discoverer = new Discoverer("http://streamfinder");
Once we have a discoverer object, we can begin to populate it with the query. In this example, the discoverer search for apple and coco.* streams in the 'fruits' group (group id 1). Since this is a js app running on the browser, we only query for streams served through ws and wss.
discoverer.discover(1, "apple").discover(1, "coco.*").using("ws").using("wss");
Retrieve the current list of streams and its sources by doing
var sources = discoverer.getSources();
The sources structure returned is a full snapshot of all available streams. sources returned from the update event contain a list of permitted streams and the connection URL for each stream. For more information about the structure of sources, see the streamfinder server API.
Users can also attach listeners to get the latest streams when they are updated
discoverer.on("update", function(sources) {
console.log(sources);
});
The update event is fired when there is a change in stream sources. The sources structure returned by the listener is a full snapshot of all available streams. This structure is similar to the getter method.
Users should also setup a listener for error events.
discoverer.on("error", function() {
console.log("Error: See console for details");
});
Common errors include starting the discoverer before a session cookie is available or incorrectly retrieving a cookie. Users should check for cookies sent when discovery requests fail.
After hooking up listeners, start the discoverer to begin receiving stream sources.
discoverer.start();