Skip to content

Commit

Permalink
feat: Allows XMPP connections to be added and removed via a REST inte…
Browse files Browse the repository at this point in the history
…rface.
  • Loading branch information
bgrozev committed Jul 19, 2018
1 parent d440d33 commit 4f3a9a9
Show file tree
Hide file tree
Showing 3 changed files with 213 additions and 0 deletions.
28 changes: 28 additions & 0 deletions doc/rest-muc-client.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
The configuration for the XMPP client connections that jitsi-videobridge uses can be modified at run time using REST calls to `/colibri/muc-client/`.

# Adding an XMPP client connection
A new XMPP client connection (i.e. a MucClient) can be added by posting a JSON which contains its configuration to `/colibri/muc-client/add`:
```
{
"id": "new-client-connection",
"domain":"xmpp.example.com",
"hostname":"10.0.0.1",
"username":"xmpp-username",
"password":"xmpp-password",
"muc_jids":"JvbBrewery@conference.xmpp.example.com",
"muc_nickname":"unique-resource",
"disable_certificate_verification":"false"
}
```

If a configuration with the specified ID already exists, the request will succeed (return 200), but the configuration will NOT be updated. If you need to update an existing configuration, you need to remove it first and then re-add it.

# Removing an XMPP client connection.
An XMPP client connection (i.e. a MucClient) can be removed by posting a JSON which contains its ID to `/colibri/muc-client/remove`:
```
{
"id": "new-client-connection"
}
```

The request will be successful (return 200) as long as the format of the JSON is as expected, regardless of whether a connection was found and removed or it wasn't found.
98 changes: 98 additions & 0 deletions src/main/java/org/jitsi/videobridge/rest/HandlerImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import org.jitsi.videobridge.*;
import org.jitsi.videobridge.health.*;
import org.jitsi.videobridge.stats.*;
import org.jitsi.videobridge.xmpp.*;
import org.jivesoftware.smack.packet.*;
import org.json.simple.*;
import org.json.simple.parser.*;
Expand Down Expand Up @@ -221,6 +222,12 @@ class HandlerImpl
*/
private static final String STATISTICS = "stats";

/**
* The HTTP resource which allows control of {@link ClientConnectionImpl}
* (i.e. adding or removing MUC clients).
*/
private static final String MUC_CLIENT = "muc-client";

static
{
String colibriTarget = DEFAULT_COLIBRI_TARGET;
Expand Down Expand Up @@ -1030,6 +1037,97 @@ else if (target.equals(SHUTDOWN))
response.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
}
}
else if (target.startsWith(MUC_CLIENT + "/"))
{
doHandleMucClientRequest(
target.substring((MUC_CLIENT + "/").length()),
request,
response);
}
}

/**
* Handles a request to /colibri/muc-client/.
* @param target the target URL with the part before "muc-client/" stripped.
* @param request the request.
* @param response the response being prepared.
*/
private void doHandleMucClientRequest(
String target,
HttpServletRequest request,
HttpServletResponse response)
{
if (!POST_HTTP_METHOD.equals(request.getMethod()))
{
response.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
return;
}

if (!RESTUtil.isJSONContentType(request.getContentType()))
{
response.setStatus(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);
return;
}

JSONObject requestJSONObject;
try
{
Object o = new JSONParser().parse(request.getReader());
if (o instanceof JSONObject)
{
requestJSONObject = (JSONObject) o;
}
else
{
requestJSONObject = null;
}
}
catch (Exception e)
{
requestJSONObject = null;
}

if (requestJSONObject == null)
{
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
return;
}

ClientConnectionImpl clientConnectionImpl
= getService(ClientConnectionImpl.class);
if (clientConnectionImpl == null)
{
response.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
return;
}

if ("add".equals(target))
{
if (clientConnectionImpl.addMucClient(requestJSONObject))
{
response.setStatus(HttpServletResponse.SC_OK);
}
else
{
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
}
}
else if ("remove".equals(target))
{
if (clientConnectionImpl.removeMucClient(requestJSONObject))
{
response.setStatus(HttpServletResponse.SC_OK);
}
else
{
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
}
}
else
{
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
return;
}
}

/**
Expand Down
87 changes: 87 additions & 0 deletions src/main/java/org/jitsi/videobridge/xmpp/ClientConnectionImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.jitsi.service.configuration.*;
import org.jitsi.xmpp.mucclient.*;
import org.jivesoftware.smack.packet.*;
import org.json.simple.*;
import org.osgi.framework.*;

import java.util.*;
Expand Down Expand Up @@ -161,4 +162,90 @@ public void setPresenceExtension(ExtensionElement extension)
{
mucClientManager.setPresenceExtension(extension);
}

/**
* Adds a new {@link MucClient} with configuration described in JSON.
* @param jsonObject the JSON which describes the configuration of the
* client.
* <p/>
* <pre>{@code
* The expected JSON format is:
* {
* "id": "muc-client-id",
* "key": "value"
* }
* }</pre>
* The [key, value] pairs are interpreted as property names and values to
* set for the client's configuration (see {@link MucClientConfiguration}).
*
* @return {@code true} if the request was successful (i.e. the JSON
* is in the required format and either a new {@link MucClient} was added
* or a client with the same ID already existed).
*/
public boolean addMucClient(JSONObject jsonObject)
{
if (jsonObject == null || !(jsonObject.get("id") instanceof String))
{
return false;
}
MucClientConfiguration config
= new MucClientConfiguration((String) jsonObject.get("id"));

for (Object key : jsonObject.keySet())
{
Object value = jsonObject.get(key);
if (key instanceof String && value instanceof String
&& !"id".equals(key))
{
config.setProperty((String) key, (String) value);
}
}

if (!config.isComplete())
{
logger.info("Not adding a MucClient, configuration incomplete.");
return false;
}
else
{
if (mucClientManager == null)
{
logger.warn("Not adding a MucClient. Not started?");
return false;
}
mucClientManager.addMucClient(config);

// We consider the case where a client with the given ID already
// exists as success. Note however, that the existing client's
// configuration was NOT modified.
return true;
}
}

/**
* Removes a {@link MucClient} with an ID described in JSON.
* @param jsonObject the JSON which contains the ID of the client to remove.
* </p>
* <pre>{@code
* The expected JSON format is:
* {
* "id": "muc-client-id",
* }
* }</pre>
*
* @return {@code false} if this instance has not been initialized, or the
* JSON is not in the expected format. Otherwise (regardless of whether
* a client with the specified ID existed or not), returns {@code true}.
*/
public boolean removeMucClient(JSONObject jsonObject)
{
if (jsonObject == null || !(jsonObject.get("id") instanceof String)
|| mucClientManager == null)
{
return false;
}

mucClientManager.removeMucClient((String) jsonObject.get("id"));
return true;
}
}

0 comments on commit 4f3a9a9

Please sign in to comment.