diff --git a/common/src/main/java/org/red5/server/net/rtmp/RTMPConnection.java b/common/src/main/java/org/red5/server/net/rtmp/RTMPConnection.java index 9104f888fafa54d5e4f4cae8431d97fd656d4e4c..eae251d4f93ecccd63817c6414bd8cf231d4c1d2 100755 --- a/common/src/main/java/org/red5/server/net/rtmp/RTMPConnection.java +++ b/common/src/main/java/org/red5/server/net/rtmp/RTMPConnection.java @@ -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()); diff --git a/common/src/main/java/org/red5/server/service/ReflectionUtils.java b/common/src/main/java/org/red5/server/service/ReflectionUtils.java index 66d15c0c04617c724d5fe06eaabecfd1e1d1b71b..0fcddd4b2f8832c9e9d1263f3584cac5bbe10f1b 100644 --- a/common/src/main/java/org/red5/server/service/ReflectionUtils.java +++ b/common/src/main/java/org/red5/server/service/ReflectionUtils.java @@ -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; @@ -27,9 +27,44 @@ public class ReflectionUtils { private static final boolean isDebug = log.isDebugEnabled(), isTrace = log.isTraceEnabled(); - //used to prevent extra object creation when a method with a set of params is not found + // 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; + } + } diff --git a/common/src/main/java/org/red5/server/service/ServiceInvoker.java b/common/src/main/java/org/red5/server/service/ServiceInvoker.java index 35ef74f1ced55b2d691c7444285e8b6efccdb789..cbe13c80b40c339c49d5ce8a9419a353a070ee1e 100644 --- a/common/src/main/java/org/red5/server/service/ServiceInvoker.java +++ b/common/src/main/java/org/red5/server/service/ServiceInvoker.java @@ -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); diff --git a/common/src/main/java/org/red5/server/so/SharedObjectScope.java b/common/src/main/java/org/red5/server/so/SharedObjectScope.java index 6697479f37426ac5d62c6f6d6fcb732804076ae8..9a1823aa0f4e43440e6ceb4e8d9beccb009d5db8 100644 --- a/common/src/main/java/org/red5/server/so/SharedObjectScope.java +++ b/common/src/main/java/org/red5/server/so/SharedObjectScope.java @@ -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]; diff --git a/io/src/main/java/org/red5/io/utils/ConversionUtils.java b/io/src/main/java/org/red5/io/utils/ConversionUtils.java index 194098905d0d6ad9de5dc775beb32288790c409e..1e7e6ee81217c61251b026b8269bf3daf02e96dc 100644 --- a/io/src/main/java/org/red5/io/utils/ConversionUtils.java +++ b/io/src/main/java/org/red5/io/utils/ConversionUtils.java @@ -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 *