Skip to content
Snippets Groups Projects
Commit fbd92c04 authored by Paul Gregoire's avatar Paul Gregoire
Browse files

Fix SO scope relection calls; refactored more reflection use

parent 8701d734
Branches
No related tags found
No related merge requests found
......@@ -78,6 +78,7 @@ import org.red5.server.stream.SingleItemSubscriberStream;
import org.red5.server.stream.StreamService;
import org.red5.server.util.ScopeUtils;
import org.springframework.core.task.TaskRejectedException;
import org.springframework.lang.Nullable;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.util.concurrent.ListenableFuture;
......@@ -1568,7 +1569,7 @@ public abstract class RTMPConnection extends BaseConnection implements IStreamCa
currentStreamTasks.removeTask(task);
}
public void onSuccess(Packet packet) {
public void onSuccess(@Nullable Packet packet) {
log.debug("ReceivedMessageTask success");
if (isDebug) {
log.debug("onSuccess - session: {}, msgType: {}, processingTime: {}, packetNum: {}", sessionId, messageType, getProcessingTime(), task.getPacketNumber());
......
......@@ -8,8 +8,8 @@
package org.red5.server.service;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import org.red5.io.utils.ConversionUtils;
......@@ -30,6 +30,41 @@ public class ReflectionUtils {
// used to prevent extra object creation when a method with a set of params is not found
private static final Object[] NULL_RETURN = new Object[] { null, null };
/**
* Method names to skip when scanning for usable application methods.
*/
private static String ignoredMethodNames = new String("equals,hashCode,toString,wait,notifyAll,getClass,clone,compareTo,finalize,notify");
// Note for .26 update is to ensure other service methods don't fail when a method is not found
// See https://github.com/Red5/red5-server/commit/d4096a4d7b35b2b92905154a9e18edea04268fb4
/**
* Returns (method, params) for the given service or method name if found on a service or scope handler. There is
* no connection argument for these calls.
*
* SharedObjectScope uses this method to find methods on the handler.
*
* @param service service to search for the method, if given
* @param methodName method name to find
* @param args arguments
* @return Method/params pairs or null if not found
*/
public static Object[] findMethod(Object service, String methodName, List<?> args) {
if (isDebug) {
log.debug("Find method: {} in service: {} args: {}", methodName, service, args);
}
// First, search for method with list parameter
Object[] methodResult = findMethodWithListParameters(service, methodName, args);
if (methodResult[0] == null) {
// Second, search for method with array parameter
methodResult = findMethodWithExactParameters(service, methodName, args.toArray());
if (methodResult[0] == null) {
log.warn("Method {} not found in {} with parameters {}", methodName, service, args);
}
}
return methodResult;
}
/**
* Returns (method, params) for the given service or method name if found on a service or scope handler.
*
......@@ -83,25 +118,6 @@ public class ReflectionUtils {
return methodResult;
}
/**
* Returns (method, params) for the given service or (null, null) if no method was found.
*
* @param service
* Service
* @param methodName
* Method name
* @param args
* Arguments
* @return Method/params pairs
*/
public static Object[] findMethodWithExactParameters(Object service, String methodName, List<?> args) {
Object[] arguments = new Object[args.size()];
for (int i = 0; i < args.size(); i++) {
arguments[i] = args.get(i);
}
return findMethodWithExactParameters(service, methodName, arguments);
}
/**
* Returns (method, params) for the given service or (null, null) if not method was found. XXX use ranking for method matching rather
* than exact type matching plus type conversion.
......@@ -136,7 +152,7 @@ public class ReflectionUtils {
} catch (NoSuchMethodException nsme) {
log.debug("Method not found using exact parameter types", nsme);
}
List<Method> methods = ConversionUtils.findMethodsByNameAndNumParams(service, methodName, numParams);
List<Method> methods = findMethodsByNameAndNumParams(service, methodName, numParams);
if (isTrace) {
log.trace("Found {} methods", methods.size());
}
......@@ -175,11 +191,7 @@ public class ReflectionUtils {
* @return Method/params pairs
*/
public static Object[] findMethodWithListParameters(Object service, String methodName, List<?> args) {
Object[] arguments = new Object[args.size()];
for (int i = 0; i < args.size(); i++) {
arguments[i] = args.get(i);
}
return findMethodWithListParameters(service, methodName, arguments);
return findMethodWithListParameters(service, methodName, args.toArray());
}
/**
......@@ -204,25 +216,18 @@ public class ReflectionUtils {
} catch (NoSuchMethodException nsme) {
log.debug("Method not found using exact parameter types", nsme);
}
List<Method> methods = ConversionUtils.findMethodsByNameAndNumParams(service, methodName, 1);
List<Method> methods = findMethodsByNameAndNumParams(service, methodName, 1);
if (isTrace) {
log.trace("Found {} methods", methods.size());
}
if (!methods.isEmpty()) {
log.debug("Multiple methods found with same name and parameter count; parameter conversion will be attempted in order");
ArrayList<Object> argsList = new ArrayList<Object>();
if (args != null) {
for (Object element : args) {
argsList.add(element);
}
}
args = new Object[] { argsList };
Object[] params = null;
for (int i = 0; i < methods.size(); i++) {
try {
method = methods.get(i);
params = ConversionUtils.convertParams(args, method.getParameterTypes());
if (argsList.size() > 0 && (argsList.get(0) instanceof IConnection) && (!(params[0] instanceof IConnection))) {
if (args.length > 0 && (args[0] instanceof IConnection) && (!(params[0] instanceof IConnection))) {
// Don't convert first IConnection parameter
continue;
}
......@@ -235,4 +240,46 @@ public class ReflectionUtils {
return NULL_RETURN;
}
/**
* Find method by name and number of parameters
*
* @param object
* Object to find method on
* @param method
* Method name
* @param numParam
* Number of parameters
* @return List of methods that match by name and number of parameters
*/
public static List<Method> findMethodsByNameAndNumParams(Object object, String method, int numParam) {
LinkedList<Method> list = new LinkedList<>();
Method[] methods = object.getClass().getMethods();
for (Method m : methods) {
String methodName = m.getName();
if (ignoredMethodNames.indexOf(methodName) > -1) {
log.debug("Skipping method: {}", methodName);
continue;
}
if (isDebug) {
Class<?>[] params = m.getParameterTypes();
log.debug("Method name: {} parameter count: {}", methodName, params.length);
for (Class<?> param : params) {
log.debug("Parameter: {}", param);
}
}
// check the name
if (!methodName.equals(method)) {
log.trace("Method name not the same");
continue;
}
// check parameters length
if (m.getParameterTypes().length != numParam) {
log.debug("Param length not the same");
continue;
}
list.add(m);
}
return list;
}
}
......@@ -12,8 +12,6 @@ import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;
import org.red5.annotations.DeclarePrivate;
import org.red5.annotations.DeclareProtected;
import org.red5.server.api.IConnection;
import org.red5.server.api.Red5;
import org.red5.server.api.scope.IScope;
......@@ -123,6 +121,7 @@ public class ServiceInvoker implements IServiceInvoker {
// get the parameters; the value at index 1 can be null, but the methodResult array will never be null
Object[] params = (Object[]) methodResult[1];
try {
/* XXX(paul) legacy flash logic for restricting access to methods
if (method.isAnnotationPresent(DeclarePrivate.class)) {
// Method may not be called by clients.
log.debug("Method {} is declared private.", method);
......@@ -134,6 +133,7 @@ public class ServiceInvoker implements IServiceInvoker {
log.debug("Client {} doesn't have required permission {} to call {}", new Object[] { conn.getClient(), annotation.permission(), method });
throw new NotAllowedException("Access denied, method is protected");
}
*/
Object result = null;
log.debug("Invoking method: {}", method.toString());
if (method.getReturnType().equals(Void.TYPE)) {
......@@ -149,13 +149,10 @@ public class ServiceInvoker implements IServiceInvoker {
((IPendingServiceCall) call).setResult(result);
}
invoked = true;
} catch (NotAllowedException e) {
} catch (NotAllowedException | IllegalAccessException e) {
call.setException(e);
call.setStatus(Call.STATUS_ACCESS_DENIED);
} catch (IllegalAccessException accessEx) {
call.setException(accessEx);
call.setStatus(Call.STATUS_ACCESS_DENIED);
log.error("Error executing call: {}", call, accessEx);
log.error("Error executing call: {}", call, e);
} catch (InvocationTargetException invocationEx) {
call.setException(invocationEx);
call.setStatus(Call.STATUS_INVOCATION_EXCEPTION);
......
......@@ -265,11 +265,7 @@ public class SharedObjectScope extends BasicScope implements ISharedObject, Stat
// Once handler is found, find matching method
if (soHandler != null) {
// With exact params...
Object[] methodResult = ReflectionUtils.findMethodWithExactParameters(soHandler, serviceMethod, arguments);
// Or at least with suitable list params
if (methodResult.length == 0 || methodResult[0] == null) {
methodResult = ReflectionUtils.findMethodWithListParameters(soHandler, serviceMethod, arguments);
}
Object[] methodResult = ReflectionUtils.findMethod(soHandler, serviceMethod, arguments);
// If method is found...
if (methodResult.length > 0 && methodResult[0] != null) {
Method method = (Method) methodResult[0];
......
......@@ -8,7 +8,6 @@
package org.red5.io.utils;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
......@@ -16,7 +15,6 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
......@@ -60,18 +58,12 @@ public class ConversionUtils {
*/
private static Map<Class<?>, Class<?>[]> parameterMap = new HashMap<Class<?>, Class<?>[]>();
/**
* Method names to skip when scanning for usable application methods.
*/
private static String ignoredMethodNames;
static {
for (int i = 0; i < PRIMITIVES.length; i++) {
primitiveMap.put(PRIMITIVES[i], WRAPPERS[i]);
wrapperMap.put(WRAPPERS[i], PRIMITIVES[i]);
parameterMap.put(WRAPPERS[i], PARAMETER_CHAINS[i]);
}
ignoredMethodNames = new String("equals,hashCode,toString,wait,notifyAll,getClass,clone,compareTo");
}
/**
......@@ -293,48 +285,6 @@ public class ConversionUtils {
throw new ConversionException(String.format("Unable to convert number to: %s", wrapper));
}
/**
* Find method by name and number of parameters
*
* @param object
* Object to find method on
* @param method
* Method name
* @param numParam
* Number of parameters
* @return List of methods that match by name and number of parameters
*/
public static List<Method> findMethodsByNameAndNumParams(Object object, String method, int numParam) {
LinkedList<Method> list = new LinkedList<Method>();
Method[] methods = object.getClass().getMethods();
for (Method m : methods) {
String methodName = m.getName();
if (ignoredMethodNames.indexOf(methodName) > -1) {
log.debug("Skipping method: {}", methodName);
continue;
}
if (log.isDebugEnabled()) {
Class<?>[] params = m.getParameterTypes();
log.debug("Method name: {} parameter count: {}", methodName, params.length);
for (Class<?> param : params) {
log.debug("Parameter: {}", param);
}
}
//check parameter length first since this should speed things up
if (m.getParameterTypes().length != numParam) {
log.debug("Param length not the same");
continue;
}
//now try to match the name
if (!methodName.equals(method)) {
log.trace("Method name not the same");
continue;
}
list.add(m);
}
return list;
}
/**
* Convert parameters using methods of this utility class
*
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment