org.red5.server.net.remoting.FlexMessagingService Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ant-media-server Show documentation
Show all versions of ant-media-server Show documentation
Ant Media Server supports RTMP, RTSP, MP4, HLS, WebRTC, Adaptive Streaming, etc.
/*
* RED5 Open Source Media Server - https://github.com/Red5/
*
* Copyright 2006-2016 by respective authors (see below). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.red5.server.net.remoting;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.apache.commons.beanutils.ConversionException;
import org.apache.commons.lang3.StringUtils;
import org.red5.compatibility.flex.data.messages.DataMessage;
import org.red5.compatibility.flex.data.messages.SequencedMessage;
import org.red5.compatibility.flex.messaging.messages.AbstractMessage;
import org.red5.compatibility.flex.messaging.messages.AcknowledgeMessage;
import org.red5.compatibility.flex.messaging.messages.AcknowledgeMessageExt;
import org.red5.compatibility.flex.messaging.messages.AsyncMessage;
import org.red5.compatibility.flex.messaging.messages.AsyncMessageExt;
import org.red5.compatibility.flex.messaging.messages.CommandMessage;
import org.red5.compatibility.flex.messaging.messages.CommandMessageExt;
import org.red5.compatibility.flex.messaging.messages.Constants;
import org.red5.compatibility.flex.messaging.messages.ErrorMessage;
import org.red5.compatibility.flex.messaging.messages.Message;
import org.red5.compatibility.flex.messaging.messages.RemotingMessage;
import org.red5.io.utils.ConversionUtils;
import org.red5.io.utils.RandomGUID;
import org.red5.server.api.IClient;
import org.red5.server.api.Red5;
import org.red5.server.api.service.IPendingServiceCall;
import org.red5.server.api.service.IServiceInvoker;
import org.red5.server.exception.ClientDetailsException;
import org.red5.server.messaging.ServiceAdapter;
import org.red5.server.service.PendingCall;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Service that can execute compatibility Flex messages.
*
* @author The Red5 Project
* @author Joachim Bauch ([email protected])
* @author Paul Gregoire ([email protected])
*/
public class FlexMessagingService {
/** Name of the service. */
public static final String SERVICE_NAME = "flexMessaging";
/**
* Logger
*/
protected static Logger log = LoggerFactory.getLogger(FlexMessagingService.class);
/** Service invoker to use. */
protected IServiceInvoker serviceInvoker;
/** Configured endpoints. */
@SuppressWarnings("unchecked")
protected Map endpoints = Collections.EMPTY_MAP;
/** Registered clients. */
protected ConcurrentMap registrations = null;
/**
* Setup available end points.
*
* @param endpoints
* end points
*/
public void setEndpoints(Map endpoints) {
this.endpoints = endpoints;
log.info("Configured endpoints: {}", endpoints);
}
/**
* Set the service invoker to use.
*
* @param serviceInvoker
* service invoker
*/
public void setServiceInvoker(IServiceInvoker serviceInvoker) {
this.serviceInvoker = serviceInvoker;
}
/**
* Construct error message.
*
* @param request
* request
* @param faultCode
* fault code
* @param faultString
* fault string
* @param faultDetail
* fault detail
* @return error message
*/
public static ErrorMessage returnError(AbstractMessage request, String faultCode, String faultString, String faultDetail) {
ErrorMessage result = new ErrorMessage();
result.timestamp = System.currentTimeMillis();
result.headers = request.headers;
result.destination = request.destination;
result.correlationId = request.messageId;
result.faultCode = faultCode;
result.faultString = faultString;
result.faultDetail = faultDetail;
return result;
}
/**
* Construct error message from exception.
*
* @param request
* request
* @param faultCode
* fault code
* @param faultString
* fautl string
* @param error
* error
* @return message
*/
public static ErrorMessage returnError(AbstractMessage request, String faultCode, String faultString, Throwable error) {
ErrorMessage result = returnError(request, faultCode, faultString, "");
if (error instanceof ClientDetailsException) {
result.extendedData = ((ClientDetailsException) error).getParameters();
if (((ClientDetailsException) error).includeStacktrace()) {
StringBuilder stack = new StringBuilder();
for (StackTraceElement element : error.getStackTrace()) {
stack.append(element.toString()).append('\n');
}
result.faultDetail = stack.toString();
}
}
result.rootCause = error;
return result;
}
/**
* Handle request coming from
*
*
* mx:RemoteObject
*
*
* tags.
*
* @see Adobe Livedocs (external)
*
* @param msg
* message
* @return aynsc message
*/
public AsyncMessage handleRequest(RemotingMessage msg) {
log.debug("Handle RemotingMessage request");
log.trace("{}", msg);
setClientId(msg);
if (serviceInvoker == null) {
log.error("No service invoker configured: {}", msg);
return returnError(msg, "Server.Invoke.Error", "No service invoker configured.", "No service invoker configured.");
}
Object endpoint = endpoints.get(msg.destination);
log.debug("End point / destination: {}", endpoint);
if (endpoint == null) {
String errMsg = String.format("Endpoint %s doesn't exist.", msg.destination);
log.debug("{} ({})", errMsg, msg);
return returnError(msg, "Server.Invoke.Error", errMsg, errMsg);
}
//prepare an ack message
AcknowledgeMessage result = new AcknowledgeMessage();
result.setClientId(msg.getClientId());
result.setCorrelationId(msg.getMessageId());
//grab any headers
Map headers = msg.getHeaders();
log.debug("Headers: {}", headers);
//if (headers.containsKey(Message.FLEX_CLIENT_ID_HEADER)) {
// headers.put(Message.FLEX_CLIENT_ID_HEADER, msg.getClientId());
//}
//result.setHeaders(headers);
//get the operation
String operation = msg.operation;
log.debug("Operation: {}", operation);
if (endpoint instanceof ServiceAdapter) {
log.debug("Endpoint is a ServiceAdapter so message will be invoked");
ServiceAdapter adapter = (ServiceAdapter) endpoint;
//the result of the invocation will make up the message body
result.body = adapter.invoke(msg);
} else {
//get arguments
Object[] args = null;
try {
log.debug("Body: {} type: {}", msg.body, msg.body.getClass().getName());
args = (Object[]) ConversionUtils.convert(msg.body, Object[].class);
} catch (ConversionException cex) {
//if the conversion fails and the endpoint is not a ServiceAdapter
//just drop the object directly into an array
args = new Object[] { msg.body };
}
IPendingServiceCall call = new PendingCall(operation, args);
try {
if (!serviceInvoker.invoke(call, endpoint)) {
if (call.getException() != null) {
// Use regular exception handling
Throwable err = call.getException();
return returnError(msg, "Server.Invoke.Error", err.getMessage(), err);
}
return returnError(msg, "Server.Invoke.Error", "Can't invoke method.", "");
}
} catch (Throwable err) {
log.error("Error while invoking method.", err);
return returnError(msg, "Server.Invoke.Error", err.getMessage(), err);
}
//we got a valid result from the method call.
result.body = call.getResult();
}
return result;
}
/**
* Handle command message (external) request.
*
* @param msg
* message
* @return message
*/
public Message handleRequest(CommandMessageExt msg) {
log.debug("Handle CommandMessageExt request");
log.trace("{}", msg);
setClientId(msg);
String clientId = msg.getClientId();
//process messages to non-service adapter end-points
switch (msg.operation) {
case Constants.POLL_OPERATION: //2
//send back modifications
log.debug("Poll: {}", clientId);
//send back stored updates for this client
if (registrations.containsKey(clientId)) {
ServiceAdapter adapter = registrations.get(clientId);
if (adapter != null) {
CommandMessage result = new CommandMessage();
result.setOperation(Constants.CLIENT_SYNC_OPERATION);
//result.setCorrelationId(msg.getMessageId());
//this will be the body of the responding command message
AsyncMessageExt ext = new AsyncMessageExt();
ext.setClientId(clientId);
ext.setCorrelationId(msg.getMessageId());
ext.setDestination("Red5Chat");
ext.setBody(adapter.manage(msg));
/*
//grab any headers
Map headers = msg.getHeaders();
log.debug("Headers: {}", headers);
if (headers.containsKey(Message.FLEX_CLIENT_ID_HEADER)) {
headers.put(Message.FLEX_CLIENT_ID_HEADER, msg.getClientId());
}
ext.setHeaders(headers);
*/
//add as a child (body) of the command message
result.setBody(new Object[] { ext });
return result;
} else {
log.warn("Adapter was not available");
}
}
break;
default:
log.error("Unhandled CommandMessageExt request: {}", msg);
String errMsg = String.format("Don't know how to handle %s", msg);
return returnError(msg, "notImplemented", errMsg, errMsg);
}
AcknowledgeMessageExt result = new AcknowledgeMessageExt();
result.setClientId(clientId);
result.setCorrelationId(msg.getMessageId());
return result;
}
/**
* Handle command message request.
*
* @param msg
* message
* @return message
*/
public Message handleRequest(CommandMessage msg) {
log.debug("Handle CommandMessage request");
log.trace("{}", msg);
setClientId(msg);
String clientId = msg.getClientId();
//grab any headers
Map headers = msg.getHeaders();
log.debug("Headers: {}", headers);
if (headers.containsKey(Message.FLEX_CLIENT_ID_HEADER)) {
headers.put(Message.FLEX_CLIENT_ID_HEADER, msg.getClientId());
}
String destination = msg.getDestination();
log.debug("Destination: {}", destination);
//process messages to non-service adapter end-points
switch (msg.operation) {
case Constants.CLIENT_PING_OPERATION: //5
//send back pong message
break;
case Constants.POLL_OPERATION: //2
//send back modifications
log.debug("Poll: {}", clientId);
//send back stored updates for this client
if (registrations.containsKey(clientId)) {
ServiceAdapter adapter = registrations.get(clientId);
if (adapter != null) {
CommandMessage result = new CommandMessage();
result.setOperation(Constants.CLIENT_SYNC_OPERATION);
//result.setCorrelationId(msg.getMessageId());
//this will be the body of the responding command message
AsyncMessageExt ext = new AsyncMessageExt();
ext.setClientId(clientId);
ext.setCorrelationId(msg.getMessageId());
ext.setDestination("Red5Chat");
ext.setBody(adapter.manage(msg));
//add as a child (body) of the command message
result.setBody(new Object[] { ext });
return result;
} else {
log.debug("Adapter was not available");
}
}
break;
case Constants.SUBSCRIBE_OPERATION: //0
log.debug("Subscribe: {}", clientId);
//if there is a destination check for an adapter
if (StringUtils.isNotBlank(destination)) {
//look-up end-point and register
if (endpoints.containsKey(destination)) {
Object endpoint = endpoints.get(destination);
//if the endpoint is an adapter, try to subscribe
if (endpoint instanceof ServiceAdapter) {
ServiceAdapter adapter = (ServiceAdapter) endpoint;
boolean subscribed = ((Boolean) adapter.manage(msg));
if (subscribed) {
log.debug("Client was subscribed");
registerClientToAdapter(clientId, adapter);
} else {
log.debug("Client was not subscribed");
}
}
}
}
// Send back registration ok
break;
case Constants.UNSUBSCRIBE_OPERATION: //1
log.trace("Unsubscribe: {}", clientId);
if (registrations.containsKey(clientId)) {
ServiceAdapter adapter = registrations.get(clientId);
boolean unsubscribed = ((Boolean) adapter.manage(msg));
if (unsubscribed) {
log.debug("Client was unsubscribed");
unregisterClientFromAdapter(clientId);
} else {
log.debug("Client was not unsubscribed");
}
} else {
log.debug("Client was not subscribed");
}
// Send back unregistration ok
break;
default:
log.error("Unknown CommandMessage request: {}", msg);
String errMsg = String.format("Don't know how to handle %s", msg);
return returnError(msg, "notImplemented", errMsg, errMsg);
}
AcknowledgeMessage result = new AcknowledgeMessage();
result.setBody(msg.getBody());
result.setClientId(clientId);
result.setCorrelationId(msg.getMessageId());
result.setHeaders(headers);
// put destination in ack if it exists
if (StringUtils.isNotBlank(destination)) {
result.setDestination(destination);
}
return result;
}
/**
* Evaluate update requests sent by a client.
*
* @param msg
* @param event
*/
@SuppressWarnings("unchecked")
private void evaluateDataUpdate(DataMessage msg, DataMessage event) {
switch (event.operation) {
case Constants.DATA_OPERATION_UPDATE_ATTRIBUTES:
List
© 2015 - 2025 Weber Informatics LLC | Privacy Policy