@Override public Object invoke(Object proxy, Method method, Object[] args) throws Exception { if (method.getDeclaringClass() == RemoteObject.class) { String name = method.getName(); if (name.equals("close")) { close(); return null; } else if (name.equals("setResponseTimeout")) { timeoutMillis = (Integer) args[0]; return null; } else if (name.equals("setNonBlocking")) { nonBlocking = (Boolean) args[0]; return null; } else if (name.equals("setTransmitReturnValue")) { transmitReturnValue = (Boolean) args[0]; return null; } else if (name.equals("setTransmitExceptions")) { transmitExceptions = (Boolean) args[0]; return null; } else if (name.equals("waitForLastResponse")) { if (lastResponseID == null) { throw new IllegalStateException("There is no last response to wait for."); } return waitForResponse(lastResponseID); } else if (name.equals("getLastResponseID")) { if (lastResponseID == null) { throw new IllegalStateException("There is no last response ID."); } return lastResponseID; } else if (name.equals("waitForResponse")) { if (!transmitReturnValue && !transmitExceptions && nonBlocking) { throw new IllegalStateException( "This RemoteObject is currently set to ignore all responses."); } return waitForResponse((Byte) args[0]); } else if (name.equals("getConnection")) { return connection; } else { // Should never happen, for debugging purposes only throw new RuntimeException( "Invocation handler could not find RemoteObject method. Check ObjectSpace.java"); } } else if (method.getDeclaringClass() == Object.class) { if (method.getName().equals("toString")) { return "<proxy>"; } try { return method.invoke(proxy, args); } catch (Exception ex) { throw new RuntimeException(ex); } } InvokeMethod invokeMethod = new InvokeMethod(); invokeMethod.objectID = objectID; invokeMethod.method = method; invokeMethod.args = args; // The only time a invocation doesn't need a response is if it's async // and no return values or exceptions are wanted back. boolean needsResponse = transmitReturnValue || transmitExceptions || !nonBlocking; if (needsResponse) { byte responseID; synchronized (this) { // Increment the response counter and put it into the first six bits of the // responseID byte responseID = nextResponseNum++; if (nextResponseNum == 64) { nextResponseNum = 1; // Keep number under 2^6, avoid 0 (see else statement // below) } } // Pack return value and exception info into the top two bits if (transmitReturnValue) { responseID |= kReturnValMask; } if (transmitExceptions) { responseID |= kReturnExMask; } invokeMethod.responseID = responseID; } else { invokeMethod.responseID = 0; // A response info of 0 means to not respond } int length = connection.sendTCP(invokeMethod); if (DEBUG) { String argString = ""; if (args != null) { argString = Arrays.deepToString(args); argString = argString.substring(1, argString.length() - 1); } debug( "kryonet", connection + " sent: " + method.getDeclaringClass().getSimpleName() + "#" + method.getName() + "(" + argString + ") (" + length + ")"); } if (invokeMethod.responseID != 0) { lastResponseID = invokeMethod.responseID; } if (nonBlocking) { Class returnType = method.getReturnType(); if (returnType.isPrimitive()) { if (returnType == int.class) { return 0; } if (returnType == boolean.class) { return Boolean.FALSE; } if (returnType == float.class) { return 0f; } if (returnType == char.class) { return (char) 0; } if (returnType == long.class) { return 0l; } if (returnType == short.class) { return (short) 0; } if (returnType == byte.class) { return (byte) 0; } if (returnType == double.class) { return 0d; } } return null; } try { Object result = waitForResponse(invokeMethod.responseID); if ((result != null) && (result instanceof Exception)) { throw (Exception) result; } else { return result; } } catch (TimeoutException ex) { throw new TimeoutException( "Response timed out: " + method.getDeclaringClass().getName() + "." + method.getName()); } }
/** * Invokes the method on the object and, if necessary, sends the result back to the connection * that made the invocation request. This method is invoked on the update thread of the {@link * EndPoint} for this ObjectSpace and unless an {@link #setExecutor(Executor) executor} has been * set. * * @param connection The remote side of this connection requested the invocation. */ protected void invoke(Connection connection, Object target, InvokeMethod invokeMethod) { if (DEBUG) { String argString = ""; if (invokeMethod.args != null) { argString = Arrays.deepToString(invokeMethod.args); argString = argString.substring(1, argString.length() - 1); } debug( "kryonet", connection + " received: " + target.getClass().getSimpleName() + "#" + invokeMethod.method.getName() + "(" + argString + ")"); } byte responseID = invokeMethod.responseID; boolean transmitReturnVal = (responseID & kReturnValMask) == kReturnValMask; boolean transmitExceptions = (responseID & kReturnExMask) == kReturnExMask; Object result = null; Method method = invokeMethod.method; try { result = method.invoke(target, invokeMethod.args); // Catch exceptions caused by the Method#invoke } catch (InvocationTargetException ex) { if (transmitExceptions) { result = ex.getCause(); } else { throw new RuntimeException( "Error invoking method: " + method.getDeclaringClass().getName() + "." + method.getName(), ex); } } catch (Exception ex) { throw new RuntimeException( "Error invoking method: " + method.getDeclaringClass().getName() + "." + method.getName(), ex); } if (responseID == 0) { return; } InvokeMethodResult invokeMethodResult = new InvokeMethodResult(); invokeMethodResult.objectID = invokeMethod.objectID; invokeMethodResult.responseID = responseID; // Do not return non-primitives if transmitReturnVal is false if (!transmitReturnVal && !invokeMethod.method.getReturnType().isPrimitive()) { invokeMethodResult.result = null; } else { invokeMethodResult.result = result; } int length = connection.sendTCP(invokeMethodResult); if (DEBUG) { debug("kryonet", connection + " sent: " + result + " (" + length + ")"); } }