1 16 package com.google.gwt.user.server.rpc; 17 18 import com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException; 19 import com.google.gwt.user.client.rpc.RemoteService; 20 import com.google.gwt.user.client.rpc.SerializationException; 21 import com.google.gwt.user.server.rpc.impl.ServerSerializableTypeOracle; 22 import com.google.gwt.user.server.rpc.impl.ServerSerializableTypeOracleImpl; 23 import com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader; 24 import com.google.gwt.user.server.rpc.impl.ServerSerializationStreamWriter; 25 26 import java.lang.reflect.InvocationTargetException ; 27 import java.lang.reflect.Method ; 28 import java.util.Arrays ; 29 import java.util.HashMap ; 30 import java.util.HashSet ; 31 import java.util.Map ; 32 import java.util.Set ; 33 34 52 public final class RPC { 53 54 57 private static final Map PRIMITIVE_WRAPPER_CLASS_TO_PRIMITIVE_CLASS = new HashMap (); 58 59 63 private static ServerSerializableTypeOracle serializableTypeOracle; 64 65 69 private static Map serviceToImplementedInterfacesMap; 70 71 private static final HashMap TYPE_NAMES; 72 static { 73 PRIMITIVE_WRAPPER_CLASS_TO_PRIMITIVE_CLASS.put(Boolean .class, Boolean.TYPE); 74 PRIMITIVE_WRAPPER_CLASS_TO_PRIMITIVE_CLASS.put(Byte .class, Byte.TYPE); 75 PRIMITIVE_WRAPPER_CLASS_TO_PRIMITIVE_CLASS.put(Character .class, 76 Character.TYPE); 77 PRIMITIVE_WRAPPER_CLASS_TO_PRIMITIVE_CLASS.put(Double .class, Double.TYPE); 78 PRIMITIVE_WRAPPER_CLASS_TO_PRIMITIVE_CLASS.put(Float .class, Float.TYPE); 79 PRIMITIVE_WRAPPER_CLASS_TO_PRIMITIVE_CLASS.put(Integer .class, Integer.TYPE); 80 PRIMITIVE_WRAPPER_CLASS_TO_PRIMITIVE_CLASS.put(Long .class, Long.TYPE); 81 PRIMITIVE_WRAPPER_CLASS_TO_PRIMITIVE_CLASS.put(Short .class, Short.TYPE); 82 83 TYPE_NAMES = new HashMap (); 84 TYPE_NAMES.put("Z", boolean.class); 85 TYPE_NAMES.put("B", byte.class); 86 TYPE_NAMES.put("C", char.class); 87 TYPE_NAMES.put("D", double.class); 88 TYPE_NAMES.put("F", float.class); 89 TYPE_NAMES.put("I", int.class); 90 TYPE_NAMES.put("J", long.class); 91 TYPE_NAMES.put("S", short.class); 92 93 serializableTypeOracle = new ServerSerializableTypeOracleImpl( 94 getPackagePaths()); 95 96 serviceToImplementedInterfacesMap = new HashMap (); 97 } 98 99 127 public static RPCRequest decodeRequest(String encodedRequest) { 128 return decodeRequest(encodedRequest, null); 129 } 130 131 168 public static RPCRequest decodeRequest(String encodedRequest, Class type) { 169 if (encodedRequest == null) { 170 throw new NullPointerException ("encodedRequest cannot be null"); 171 } 172 173 if (encodedRequest.length() == 0) { 174 throw new IllegalArgumentException ("encodedRequest cannot be empty"); 175 } 176 177 try { 178 ServerSerializationStreamReader streamReader = new ServerSerializationStreamReader( 179 serializableTypeOracle); 180 streamReader.prepareToRead(encodedRequest); 181 182 String serviceIntfName = streamReader.readString(); 183 184 if (type != null) { 185 if (!implementsInterface(type, serviceIntfName)) { 186 throw new IncompatibleRemoteServiceException( 188 "Blocked attempt to access interface '" + serviceIntfName 189 + "', which is not implemented by '" + printTypeName(type) 190 + "'; this is either misconfiguration or a hack attempt"); 191 } 192 } 193 194 Class serviceIntf; 195 try { 196 serviceIntf = getClassFromSerializedName(serviceIntfName); 197 if (!RemoteService.class.isAssignableFrom(serviceIntf)) { 198 throw new IncompatibleRemoteServiceException( 200 "Blocked attempt to access interface '" 201 + printTypeName(serviceIntf) 202 + "', which doesn't extend RemoteService; this is either misconfiguration or a hack attempt"); 203 } 204 } catch (ClassNotFoundException e) { 205 throw new IncompatibleRemoteServiceException( 206 "Could not locate requested interface '" + serviceIntfName 207 + "' in default classloader", e); 208 } 209 210 String serviceMethodName = streamReader.readString(); 211 212 int paramCount = streamReader.readInt(); 213 Class [] parameterTypes = new Class [paramCount]; 214 215 for (int i = 0; i < parameterTypes.length; i++) { 216 String paramClassName = streamReader.readString(); 217 try { 218 parameterTypes[i] = getClassFromSerializedName(paramClassName); 219 } catch (ClassNotFoundException e) { 220 throw new IncompatibleRemoteServiceException("Parameter " + i 221 + " of is of an unknown type '" + paramClassName + "'", e); 222 } 223 } 224 225 Method method = findInterfaceMethod(serviceIntf, serviceMethodName, 226 parameterTypes, true); 227 228 if (method == null) { 229 throw new IncompatibleRemoteServiceException( 230 formatMethodNotFoundErrorMessage(serviceIntf, serviceMethodName, 231 parameterTypes)); 232 } 233 234 Object [] parameterValues = new Object [parameterTypes.length]; 235 for (int i = 0; i < parameterValues.length; i++) { 236 parameterValues[i] = streamReader.deserializeValue(parameterTypes[i]); 237 } 238 239 return new RPCRequest(method, parameterValues); 240 241 } catch (SerializationException ex) { 242 throw new IncompatibleRemoteServiceException(ex.getMessage(), ex); 243 } 244 } 245 246 261 public static String encodeResponseForFailure(Method serviceMethod, 262 Throwable cause) throws SerializationException { 263 if (cause == null) { 264 throw new NullPointerException ("cause cannot be null"); 265 } 266 267 if (serviceMethod != null && !RPC.isExpectedException(serviceMethod, cause)) { 268 throw new UnexpectedException("Service method '" 269 + getSourceRepresentation(serviceMethod) 270 + "' threw an unexpected exception: " + cause.toString(), cause); 271 } 272 273 return encodeResponse(cause.getClass(), cause, true); 274 } 275 276 290 public static String encodeResponseForSuccess(Method serviceMethod, 291 Object object) throws SerializationException { 292 if (serviceMethod == null) { 293 throw new NullPointerException ("serviceMethod cannot be null"); 294 } 295 296 Class methodReturnType = serviceMethod.getReturnType(); 297 if (methodReturnType != void.class && object != null) { 298 Class actualReturnType; 299 if (methodReturnType.isPrimitive()) { 300 actualReturnType = getPrimitiveClassFromWrapper(object.getClass()); 301 } else { 302 actualReturnType = object.getClass(); 303 } 304 305 if (actualReturnType == null 306 || !methodReturnType.isAssignableFrom(actualReturnType)) { 307 throw new IllegalArgumentException ("Type '" 308 + printTypeName(object.getClass()) 309 + "' does not match the return type in the method's signature: '" 310 + getSourceRepresentation(serviceMethod) + "'"); 311 } 312 } 313 314 return encodeResponse(methodReturnType, object, false); 315 } 316 317 339 public static String invokeAndEncodeResponse(Object target, 340 Method serviceMethod, Object [] args) throws SerializationException { 341 342 if (serviceMethod == null) { 343 throw new NullPointerException ("serviceMethod cannot be null"); 344 } 345 346 String responsePayload; 347 try { 348 Object result = serviceMethod.invoke(target, args); 349 350 responsePayload = encodeResponseForSuccess(serviceMethod, result); 351 } catch (IllegalAccessException e) { 352 SecurityException securityException = new SecurityException ( 353 formatIllegalAccessErrorMessage(target, serviceMethod)); 354 securityException.initCause(e); 355 throw securityException; 356 } catch (IllegalArgumentException e) { 357 SecurityException securityException = new SecurityException ( 358 formatIllegalArgumentErrorMessage(target, serviceMethod, args)); 359 securityException.initCause(e); 360 throw securityException; 361 } catch (InvocationTargetException e) { 362 Throwable cause = e.getCause(); 365 366 responsePayload = encodeResponseForFailure(serviceMethod, cause); 367 } 368 369 return responsePayload; 370 } 371 372 384 private static String encodeResponse(Class responseClass, Object object, 385 boolean wasThrown) throws SerializationException { 386 387 ServerSerializationStreamWriter stream = new ServerSerializationStreamWriter( 388 serializableTypeOracle); 389 390 stream.prepareToWrite(); 391 if (responseClass != void.class) { 392 stream.serializeValue(object, responseClass); 393 } 394 395 String bufferStr = (wasThrown ? "//EX" : "//OK") + stream.toString(); 396 return bufferStr; 397 } 398 399 402 private static Method findInterfaceMethod(Class intf, String methodName, 403 Class [] paramTypes, boolean includeInherited) { 404 try { 405 return intf.getDeclaredMethod(methodName, paramTypes); 406 } catch (NoSuchMethodException e) { 407 if (includeInherited) { 408 Class [] superintfs = intf.getInterfaces(); 409 for (int i = 0; i < superintfs.length; i++) { 410 Method method = findInterfaceMethod(superintfs[i], methodName, 411 paramTypes, true); 412 if (method != null) { 413 return method; 414 } 415 } 416 } 417 418 return null; 419 } 420 } 421 422 private static String formatIllegalAccessErrorMessage(Object target, 423 Method serviceMethod) { 424 StringBuffer sb = new StringBuffer (); 425 sb.append("Blocked attempt to access inaccessible method '"); 426 sb.append(getSourceRepresentation(serviceMethod)); 427 sb.append("'"); 428 429 if (target != null) { 430 sb.append(" on target '"); 431 sb.append(printTypeName(target.getClass())); 432 sb.append("'"); 433 } 434 435 sb.append("; this is either misconfiguration or a hack attempt"); 436 437 return sb.toString(); 438 } 439 440 private static String formatIllegalArgumentErrorMessage(Object target, 441 Method serviceMethod, Object [] args) { 442 StringBuffer sb = new StringBuffer (); 443 sb.append("Blocked attempt to invoke method '"); 444 sb.append(getSourceRepresentation(serviceMethod)); 445 sb.append("'"); 446 447 if (target != null) { 448 sb.append(" on target '"); 449 sb.append(printTypeName(target.getClass())); 450 sb.append("'"); 451 } 452 453 sb.append(" with invalid arguments"); 454 455 if (args != null && args.length > 0) { 456 sb.append(Arrays.asList(args)); 457 } 458 459 return sb.toString(); 460 } 461 462 private static String formatMethodNotFoundErrorMessage(Class serviceIntf, 463 String serviceMethodName, Class [] parameterTypes) { 464 StringBuffer sb = new StringBuffer (); 465 466 sb.append("Could not locate requested method '"); 467 sb.append(serviceMethodName); 468 sb.append("("); 469 for (int i = 0; i < parameterTypes.length; ++i) { 470 if (i > 0) { 471 sb.append(", "); 472 } 473 sb.append(printTypeName(parameterTypes[i])); 474 } 475 sb.append(")'"); 476 477 sb.append(" in interface '"); 478 sb.append(printTypeName(serviceIntf)); 479 sb.append("'"); 480 481 return sb.toString(); 482 } 483 484 491 private static Class getClassFromSerializedName(String serializedName) 492 throws ClassNotFoundException { 493 Object value = TYPE_NAMES.get(serializedName); 494 if (value != null) { 495 return (Class ) value; 496 } 497 498 return Class.forName(serializedName, false, RPC.class.getClassLoader()); 499 } 500 501 510 private static String [] getPackagePaths() { 511 return new String [] {"com.google.gwt.user.client.rpc.core"}; 512 } 513 514 521 private static Class getPrimitiveClassFromWrapper(Class wrapperClass) { 522 return (Class ) PRIMITIVE_WRAPPER_CLASS_TO_PRIMITIVE_CLASS.get(wrapperClass); 523 } 524 525 531 private static String getSourceRepresentation(Method method) { 532 return method.toString().replace('$', '.'); 533 } 534 535 539 private static boolean implementsInterface(Class service, String intfName) { 540 synchronized (serviceToImplementedInterfacesMap) { 541 Set interfaceSet = (Set ) serviceToImplementedInterfacesMap.get(service); 544 if (interfaceSet != null) { 545 if (interfaceSet.contains(intfName)) { 546 return true; 547 } 548 } else { 549 interfaceSet = new HashSet (); 550 serviceToImplementedInterfacesMap.put(service, interfaceSet); 551 } 552 553 if (!service.isInterface()) { 554 while ((service != null) && !RemoteServiceServlet.class.equals(service)) { 555 Class [] intfs = service.getInterfaces(); 556 for (int i = 0; i < intfs.length; i++) { 557 Class intf = intfs[i]; 558 if (implementsInterfaceRecursive(intf, intfName)) { 559 interfaceSet.add(intfName); 560 return true; 561 } 562 } 563 564 service = service.getSuperclass(); 568 } 569 } else { 570 if (implementsInterfaceRecursive(service, intfName)) { 571 interfaceSet.add(intfName); 572 return true; 573 } 574 } 575 576 return false; 577 } 578 } 579 580 583 private static boolean implementsInterfaceRecursive(Class clazz, 584 String intfName) { 585 assert (clazz.isInterface()); 586 587 if (clazz.getName().equals(intfName)) { 588 return true; 589 } 590 591 Class [] intfs = clazz.getInterfaces(); 593 for (int i = 0; i < intfs.length; i++) { 594 Class intf = intfs[i]; 595 if (implementsInterfaceRecursive(intf, intfName)) { 596 return true; 597 } 598 } 599 600 return false; 601 } 602 603 614 private static boolean isExpectedException(Method serviceIntfMethod, 615 Throwable cause) { 616 assert (serviceIntfMethod != null); 617 assert (cause != null); 618 619 Class [] exceptionsThrown = serviceIntfMethod.getExceptionTypes(); 620 if (exceptionsThrown.length <= 0) { 621 return false; 624 } 625 626 Class causeType = cause.getClass(); 627 628 for (int index = 0; index < exceptionsThrown.length; ++index) { 629 Class exceptionThrown = exceptionsThrown[index]; 630 assert (exceptionThrown != null); 631 632 if (exceptionThrown.isAssignableFrom(causeType)) { 633 return true; 634 } 635 } 636 637 return false; 638 } 639 640 645 private static String printTypeName(Class type) { 646 if (type.equals(Integer.TYPE)) { 649 return "int"; 650 } else if (type.equals(Long.TYPE)) { 651 return "long"; 652 } else if (type.equals(Short.TYPE)) { 653 return "short"; 654 } else if (type.equals(Byte.TYPE)) { 655 return "byte"; 656 } else if (type.equals(Character.TYPE)) { 657 return "char"; 658 } else if (type.equals(Boolean.TYPE)) { 659 return "boolean"; 660 } else if (type.equals(Float.TYPE)) { 661 return "float"; 662 } else if (type.equals(Double.TYPE)) { 663 return "double"; 664 } 665 666 if (type.isArray()) { 669 Class componentType = type.getComponentType(); 670 return printTypeName(componentType) + "[]"; 671 } 672 673 return type.getName().replace('$', '.'); 676 } 677 678 681 private RPC() { 682 } 684 } 685 | Popular Tags |