IDLTypesUtil.java revision 608:7e06bf1dcb09
1/* 2 * Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 27package com.sun.corba.se.impl.presentation.rmi ; 28 29import java.lang.reflect.Method; 30import java.lang.reflect.Field; 31import java.util.Set; 32import java.util.HashSet; 33import java.util.Iterator; 34 35/** 36 * Utility class for testing RMI/IDL Types as defined in 37 * Section 1.2 of The Java Language to IDL Mapping. Note that 38 * these are static checks only. Runtime checks, such as those 39 * described in Section 1.2.3, #3, are not covered. 40 */ 41public final class IDLTypesUtil { 42 43 private static final String GET_PROPERTY_PREFIX = "get"; 44 private static final String SET_PROPERTY_PREFIX = "set"; 45 private static final String IS_PROPERTY_PREFIX = "is"; 46 47 public static final int VALID_TYPE = 0; 48 public static final int INVALID_TYPE = 1; 49 50 /* rmic -iiop does not correctly implement the clause in 1.3.4.3 51 * about is<NAME>/get<NAME> conflicts. The spec says that 52 * is<NAME> is the property and get<NAME> is left alone, 53 * but rmic does the opposite. We will follow rmic in this, 54 * but it's easy to change. 55 */ 56 public static final boolean FOLLOW_RMIC = true ; 57 58 /** 59 * Validate a class to ensure it conforms to the rules for a 60 * Java RMI/IIOP interface. 61 * 62 * @throws IDLTypeException if not a valid RMI/IIOP interface. 63 */ 64 public void validateRemoteInterface(Class c) throws IDLTypeException 65 { 66 if( c == null ) { 67 throw new IllegalArgumentException(); 68 } 69 70 if( !c.isInterface() ) { 71 String msg = "Class " + c + " must be a java interface."; 72 throw new IDLTypeException(msg); 73 } 74 75 if( !java.rmi.Remote.class.isAssignableFrom(c) ) { 76 String msg = "Class " + c + " must extend java.rmi.Remote, " + 77 "either directly or indirectly."; 78 throw new IDLTypeException(msg); 79 } 80 81 // Get all methods, including super-interface methods. 82 Method[] methods = c.getMethods(); 83 84 for(int i = 0; i < methods.length; i++) { 85 Method next = methods[i]; 86 validateExceptions(next); 87 } 88 89 // Removed because of bug 4989053 90 // validateDirectInterfaces(c); 91 validateConstants(c); 92 93 return; 94 } 95 96 public boolean isRemoteInterface(Class c) 97 { 98 boolean remoteInterface = true; 99 try { 100 validateRemoteInterface(c); 101 } catch(IDLTypeException ite) { 102 remoteInterface = false; 103 } 104 105 return remoteInterface; 106 } 107 108 /** 109 * Section 1.2.2 Primitive Types 110 */ 111 public boolean isPrimitive(Class c) 112 { 113 if( c == null ) { 114 throw new IllegalArgumentException(); 115 } 116 117 return c.isPrimitive(); 118 } 119 120 /** 121 * Section 1.2.4 122 */ 123 public boolean isValue(Class c) 124 { 125 if( c == null ) { 126 throw new IllegalArgumentException(); 127 } 128 129 return 130 (!c.isInterface() && 131 java.io.Serializable.class.isAssignableFrom(c) && 132 !java.rmi.Remote.class.isAssignableFrom(c)); 133 } 134 135 /** 136 * Section 1.2.5 137 */ 138 public boolean isArray(Class c) 139 { 140 boolean arrayType = false; 141 142 if( c == null ) { 143 throw new IllegalArgumentException(); 144 } 145 146 if( c.isArray() ) { 147 Class componentType = c.getComponentType(); 148 arrayType = 149 (isPrimitive(componentType) || isRemoteInterface(componentType) || 150 isEntity(componentType) || isException(componentType) || 151 isValue(componentType) || isObjectReference(componentType) ); 152 } 153 154 return arrayType; 155 } 156 157 /** 158 * Section 1.2.6 159 */ 160 public boolean isException(Class c) 161 { 162 if( c == null ) { 163 throw new IllegalArgumentException(); 164 } 165 166 // Must be a checked exception, not including RemoteException or 167 // its subclasses. 168 return isCheckedException(c) && !isRemoteException(c) && isValue(c); 169 } 170 171 public boolean isRemoteException(Class c) 172 { 173 if( c == null ) { 174 throw new IllegalArgumentException(); 175 } 176 177 return java.rmi.RemoteException.class.isAssignableFrom(c) ; 178 } 179 180 public boolean isCheckedException(Class c) 181 { 182 if( c == null ) { 183 throw new IllegalArgumentException(); 184 } 185 186 return Throwable.class.isAssignableFrom(c) && 187 !RuntimeException.class.isAssignableFrom(c) && 188 !Error.class.isAssignableFrom(c) ; 189 } 190 191 /** 192 * Section 1.2.7 193 */ 194 public boolean isObjectReference(Class c) 195 { 196 if( c == null ) { 197 throw new IllegalArgumentException(); 198 } 199 200 return (c.isInterface() && 201 org.omg.CORBA.Object.class.isAssignableFrom(c)); 202 } 203 204 /** 205 * Section 1.2.8 206 */ 207 public boolean isEntity(Class c) 208 { 209 if( c == null ) { 210 throw new IllegalArgumentException(); 211 } 212 213 Class superClass = c.getSuperclass(); 214 return (!c.isInterface() && 215 (superClass != null) && 216 (org.omg.CORBA.portable.IDLEntity.class.isAssignableFrom(c))); 217 } 218 219 /** 220 * Return true if given method is legal property accessor as defined in 221 * Section 1.3.4.3 of Java2IDL spec. 222 */ 223 public boolean isPropertyAccessorMethod(Method m, Class c) { 224 225 String methodName = m.getName(); 226 Class returnType = m.getReturnType(); 227 Class[] parameters = m.getParameterTypes(); 228 Class[] exceptionTypes = m.getExceptionTypes(); 229 String propertyType = null; 230 231 if( methodName.startsWith(GET_PROPERTY_PREFIX) ) { 232 233 if((parameters.length == 0) && (returnType != Void.TYPE) && 234 !readHasCorrespondingIsProperty(m, c)) { 235 propertyType = GET_PROPERTY_PREFIX; 236 } 237 238 } else if( methodName.startsWith(SET_PROPERTY_PREFIX) ) { 239 240 if((returnType == Void.TYPE) && (parameters.length == 1)) { 241 if (hasCorrespondingReadProperty(m, c, GET_PROPERTY_PREFIX) || 242 hasCorrespondingReadProperty(m, c, IS_PROPERTY_PREFIX)) { 243 propertyType = SET_PROPERTY_PREFIX; 244 } 245 } 246 247 } else if( methodName.startsWith(IS_PROPERTY_PREFIX) ) { 248 if((parameters.length == 0) && (returnType == Boolean.TYPE) && 249 !isHasCorrespondingReadProperty(m, c)) { 250 propertyType = IS_PROPERTY_PREFIX; 251 } 252 } 253 254 // Some final checks that apply to all properties. 255 if( propertyType != null ) { 256 if(!validPropertyExceptions(m) || 257 (methodName.length() <= propertyType.length())) { 258 propertyType = null; 259 } 260 } 261 262 return (propertyType != null); 263 } 264 265 private boolean hasCorrespondingReadProperty 266 (Method writeProperty, Class c, String readPropertyPrefix) 267 { 268 String writePropertyMethodName = writeProperty.getName(); 269 Class[] writePropertyParameters = writeProperty.getParameterTypes(); 270 boolean foundReadProperty = false; 271 272 try { 273 // Look for a valid corresponding Read property 274 String readPropertyMethodName = 275 writePropertyMethodName.replaceFirst 276 (SET_PROPERTY_PREFIX, readPropertyPrefix); 277 Method readPropertyMethod = c.getMethod(readPropertyMethodName, 278 new Class[] {}); 279 foundReadProperty = 280 ( isPropertyAccessorMethod(readPropertyMethod, c) && 281 (readPropertyMethod.getReturnType() == 282 writePropertyParameters[0]) ); 283 } catch(Exception e) { 284 // ignore. this means we didn't find a corresponding get property. 285 } 286 287 return foundReadProperty; 288 } 289 290 private boolean readHasCorrespondingIsProperty(Method readProperty, 291 Class c) 292 { 293 if (FOLLOW_RMIC) 294 return false ; 295 296 String readPropertyMethodName = readProperty.getName(); 297 boolean foundIsProperty = false; 298 299 try { 300 // Look for a valid corresponding Is property 301 String isPropertyMethodName = 302 readPropertyMethodName.replaceFirst(GET_PROPERTY_PREFIX, 303 IS_PROPERTY_PREFIX); 304 Method isPropertyMethod = c.getMethod( isPropertyMethodName, 305 new Class[] {}); 306 foundIsProperty = isPropertyAccessorMethod(isPropertyMethod, 307 c) ; 308 } catch(Exception e) { 309 // ignore. this means we didn't find a corresponding Is property. 310 } 311 312 return foundIsProperty; 313 } 314 315 private boolean isHasCorrespondingReadProperty(Method readProperty, 316 Class c) 317 { 318 if (!FOLLOW_RMIC) 319 return false ; 320 321 String readPropertyMethodName = readProperty.getName(); 322 boolean foundIsProperty = false; 323 324 try { 325 // Look for a valid corresponding Read property 326 String isPropertyMethodName = 327 readPropertyMethodName.replaceFirst(IS_PROPERTY_PREFIX, 328 GET_PROPERTY_PREFIX); 329 Method isPropertyMethod = c.getMethod( isPropertyMethodName, 330 new Class[] {}); 331 foundIsProperty = isPropertyAccessorMethod(isPropertyMethod, 332 c) ; 333 } catch(Exception e) { 334 // ignore. this means we didn't find a corresponding read property. 335 } 336 337 return foundIsProperty; 338 } 339 340 public String getAttributeNameForProperty(String propertyName) { 341 String attributeName = null; 342 String prefix = null; 343 344 if( propertyName.startsWith(GET_PROPERTY_PREFIX) ) { 345 prefix = GET_PROPERTY_PREFIX; 346 } else if( propertyName.startsWith(SET_PROPERTY_PREFIX) ) { 347 prefix = SET_PROPERTY_PREFIX; 348 } else if( propertyName.startsWith(IS_PROPERTY_PREFIX) ) { 349 prefix = IS_PROPERTY_PREFIX; 350 } 351 352 if( (prefix != null) && (prefix.length() < propertyName.length()) ) { 353 String remainder = propertyName.substring(prefix.length()); 354 if( (remainder.length() >= 2) && 355 Character.isUpperCase(remainder.charAt(0)) && 356 Character.isUpperCase(remainder.charAt(1)) ) { 357 // don't set the first letter to lower-case if the 358 // first two are upper-case 359 attributeName = remainder; 360 } else { 361 attributeName = Character.toLowerCase(remainder.charAt(0)) + 362 remainder.substring(1); 363 } 364 } 365 366 return attributeName; 367 } 368 369 /** 370 * Return IDL Type name for primitive types as defined in 371 * Section 1.3.3 of Java2IDL spec or null if not a primitive type. 372 */ 373 public IDLType getPrimitiveIDLTypeMapping(Class c) { 374 375 if( c == null ) { 376 throw new IllegalArgumentException(); 377 } 378 379 if( c.isPrimitive() ) { 380 if( c == Void.TYPE ) { 381 return new IDLType( c, "void" ) ; 382 } else if( c == Boolean.TYPE ) { 383 return new IDLType( c, "boolean" ) ; 384 } else if( c == Character.TYPE ) { 385 return new IDLType( c, "wchar" ) ; 386 } else if( c == Byte.TYPE ) { 387 return new IDLType( c, "octet" ) ; 388 } else if( c == Short.TYPE ) { 389 return new IDLType( c, "short" ) ; 390 } else if( c == Integer.TYPE ) { 391 return new IDLType( c, "long" ) ; 392 } else if( c == Long.TYPE ) { 393 return new IDLType( c, "long_long" ) ; 394 } else if( c == Float.TYPE ) { 395 return new IDLType( c, "float" ) ; 396 } else if( c == Double.TYPE ) { 397 return new IDLType( c, "double" ) ; 398 } 399 } 400 401 return null; 402 } 403 404 /** 405 * Return IDL Type name for special case type mappings as defined in 406 * Table 1-1 of Java2IDL spec or null if given class is not a special 407 * type. 408 */ 409 public IDLType getSpecialCaseIDLTypeMapping(Class c) { 410 411 if( c == null ) { 412 throw new IllegalArgumentException(); 413 } 414 415 if( c == java.lang.Object.class ) { 416 return new IDLType( c, new String[] { "java", "lang" }, 417 "Object" ) ; 418 } else if( c == java.lang.String.class ) { 419 return new IDLType( c, new String[] { "CORBA" }, 420 "WStringValue" ) ; 421 } else if( c == java.lang.Class.class ) { 422 return new IDLType( c, new String[] { "javax", "rmi", "CORBA" }, 423 "ClassDesc" ) ; 424 } else if( c == java.io.Serializable.class ) { 425 return new IDLType( c, new String[] { "java", "io" }, 426 "Serializable" ) ; 427 } else if( c == java.io.Externalizable.class ) { 428 return new IDLType( c, new String[] { "java", "io" }, 429 "Externalizable" ) ; 430 } else if( c == java.rmi.Remote.class ) { 431 return new IDLType( c, new String[] { "java", "rmi" }, 432 "Remote" ) ; 433 } else if( c == org.omg.CORBA.Object.class ) { 434 return new IDLType( c, "Object" ) ; 435 } else { 436 return null; 437 } 438 } 439 440 /** 441 * Implements 1.2.3 #2 and #4 442 */ 443 private void validateExceptions(Method method) throws IDLTypeException { 444 445 Class[] exceptions = method.getExceptionTypes(); 446 447 boolean declaresRemoteExceptionOrSuperClass = false; 448 449 // Section 1.2.3, #2 450 for(int eIndex = 0; eIndex < exceptions.length; eIndex++) { 451 Class exception = exceptions[eIndex]; 452 if( isRemoteExceptionOrSuperClass(exception) ) { 453 declaresRemoteExceptionOrSuperClass = true; 454 break; 455 } 456 } 457 458 if( !declaresRemoteExceptionOrSuperClass ) { 459 String msg = "Method '" + method + "' must throw at least one " + 460 "exception of type java.rmi.RemoteException or one of its " + 461 "super-classes"; 462 throw new IDLTypeException(msg); 463 } 464 465 // Section 1.2.3, #4 466 // See also bug 4972402 467 // For all exceptions E in exceptions, 468 // (isCheckedException(E) => (isValue(E) || RemoteException.isAssignableFrom( E ) ) 469 for(int eIndex = 0; eIndex < exceptions.length; eIndex++) { 470 Class exception = exceptions[eIndex]; 471 472 if (isCheckedException(exception) && !isValue(exception) && 473 !isRemoteException(exception)) 474 { 475 String msg = "Exception '" + exception + "' on method '" + 476 method + "' is not a allowed RMI/IIOP exception type"; 477 throw new IDLTypeException(msg); 478 } 479 } 480 481 return; 482 } 483 484 /** 485 * Returns true if the method's throw clause conforms to the exception 486 * restrictions for properties as defined in Section 1.3.4.3 of 487 * Java2IDL spec. This means that for all exceptions E declared on the 488 * method, E isChecked => RemoteException.isAssignableFrom( E ). 489 */ 490 private boolean validPropertyExceptions(Method method) 491 { 492 Class[] exceptions = method.getExceptionTypes(); 493 494 for(int eIndex = 0; eIndex < exceptions.length; eIndex++) { 495 Class exception = exceptions[eIndex]; 496 497 if (isCheckedException(exception) && !isRemoteException(exception)) 498 return false ; 499 } 500 501 return true; 502 } 503 504 /** 505 * Implements Section 1.2.3, #2. 506 */ 507 private boolean isRemoteExceptionOrSuperClass(Class c) { 508 return 509 ((c == java.rmi.RemoteException.class) || 510 (c == java.io.IOException.class) || 511 (c == java.lang.Exception.class) || 512 (c == java.lang.Throwable.class)); 513 } 514 515 /** 516 * Implements Section 1.2.3, #5. 517 */ 518 private void validateDirectInterfaces(Class c) throws IDLTypeException { 519 520 Class[] directInterfaces = c.getInterfaces(); 521 522 if( directInterfaces.length < 2 ) { 523 return; 524 } 525 526 Set allMethodNames = new HashSet(); 527 Set currentMethodNames = new HashSet(); 528 529 for(int i = 0; i < directInterfaces.length; i++) { 530 Class next = directInterfaces[i]; 531 Method[] methods = next.getMethods(); 532 533 // Comparison is based on method names only. First collect 534 // all methods from current interface, eliminating duplicate 535 // names. 536 currentMethodNames.clear(); 537 for(int m = 0; m < methods.length; m++) { 538 currentMethodNames.add(methods[m].getName()); 539 } 540 541 // Now check each method against list of all unique method 542 // names processed so far. 543 for(Iterator iter=currentMethodNames.iterator(); iter.hasNext();) { 544 String methodName = (String) iter.next(); 545 if( allMethodNames.contains(methodName) ) { 546 String msg = "Class " + c + " inherits method " + 547 methodName + " from multiple direct interfaces."; 548 throw new IDLTypeException(msg); 549 } else { 550 allMethodNames.add(methodName); 551 } 552 } 553 } 554 555 return; 556 } 557 558 /** 559 * Implements 1.2.3 #6 560 */ 561 private void validateConstants(final Class c) 562 throws IDLTypeException { 563 564 Field[] fields = null; 565 566 try { 567 fields = (Field[]) 568 java.security.AccessController.doPrivileged 569 (new java.security.PrivilegedExceptionAction() { 570 public java.lang.Object run() throws Exception { 571 return c.getFields(); 572 } 573 }); 574 } catch(java.security.PrivilegedActionException pae) { 575 IDLTypeException ite = new IDLTypeException(); 576 ite.initCause(pae); 577 throw ite; 578 } 579 580 for(int i = 0; i < fields.length; i++) { 581 Field next = fields[i]; 582 Class fieldType = next.getType(); 583 if( (fieldType != java.lang.String.class) && 584 !isPrimitive(fieldType) ) { 585 String msg = "Constant field '" + next.getName() + 586 "' in class '" + next.getDeclaringClass().getName() + 587 "' has invalid type' " + next.getType() + "'. Constants" + 588 " in RMI/IIOP interfaces can only have primitive" + 589 " types and java.lang.String types."; 590 throw new IDLTypeException(msg); 591 } 592 } 593 594 595 return; 596 } 597 598} 599