/* * Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package org.jboss.com.sun.corba.se.impl.presentation.rmi; import java.lang.reflect.Field; import java.lang.reflect.Method; /** * Utility class for testing RMI/IDL Types as defined in Section 1.2 of The Java Language to IDL Mapping. Note that * these are static checks only. Runtime checks, such as those described in Section 1.2.3, #3, are not covered. */ public final class IDLTypesUtil { private static final String GET_PROPERTY_PREFIX = "get"; private static final String SET_PROPERTY_PREFIX = "set"; private static final String IS_PROPERTY_PREFIX = "is"; public static final int VALID_TYPE = 0; public static final int INVALID_TYPE = 1; /* * rmic -iiop does not correctly implement the clause in 1.3.4.3 about is<NAME>/get<NAME> conflicts. The spec says * that is<NAME> is the property and get<NAME> is left alone, but rmic does the opposite. We will follow rmic in * this, but it's easy to change. */ public static final boolean FOLLOW_RMIC = true; /** * Validate a class to ensure it conforms to the rules for a Java RMI/IIOP interface. * * @throws IDLTypeException * if not a valid RMI/IIOP interface. */ public void validateRemoteInterface(Class<?> c) throws IDLTypeException { if (c == null) { throw new IllegalArgumentException(); } if (!c.isInterface()) { String msg = "Class " + c + " must be a java interface."; throw new IDLTypeException(msg); } if (!java.rmi.Remote.class.isAssignableFrom(c)) { String msg = "Class " + c + " must extend java.rmi.Remote, " + "either directly or indirectly."; throw new IDLTypeException(msg); } // Get all methods, including super-interface methods. Method[] methods = c.getMethods(); for (int i = 0; i < methods.length; i++) { Method next = methods[i]; validateExceptions(next); } // Removed because of bug 4989053 // validateDirectInterfaces(c); validateConstants(c); return; } public boolean isRemoteInterface(Class<?> c) { boolean remoteInterface = true; try { validateRemoteInterface(c); } catch (IDLTypeException ite) { remoteInterface = false; } return remoteInterface; } /** * Section 1.2.2 Primitive Types */ public boolean isPrimitive(Class<?> c) { if (c == null) { throw new IllegalArgumentException(); } return c.isPrimitive(); } /** * Section 1.2.4 */ public boolean isValue(Class<?> c) { if (c == null) { throw new IllegalArgumentException(); } return (!c.isInterface() && java.io.Serializable.class.isAssignableFrom(c) && !java.rmi.Remote.class .isAssignableFrom(c)); } /** * Section 1.2.5 */ public boolean isArray(Class<?> c) { boolean arrayType = false; if (c == null) { throw new IllegalArgumentException(); } if (c.isArray()) { Class<?> componentType = c.getComponentType(); arrayType = (isPrimitive(componentType) || isRemoteInterface(componentType) || isEntity(componentType) || isException(componentType) || isValue(componentType) || isObjectReference(componentType)); } return arrayType; } /** * Section 1.2.6 */ public boolean isException(Class<?> c) { if (c == null) { throw new IllegalArgumentException(); } // Must be a checked exception, not including RemoteException or // its subclasses. return isCheckedException(c) && !isRemoteException(c) && isValue(c); } public boolean isRemoteException(Class<?> c) { if (c == null) { throw new IllegalArgumentException(); } return java.rmi.RemoteException.class.isAssignableFrom(c); } public boolean isCheckedException(Class<?> c) { if (c == null) { throw new IllegalArgumentException(); } return Throwable.class.isAssignableFrom(c) && !RuntimeException.class.isAssignableFrom(c) && !Error.class.isAssignableFrom(c); } /** * Section 1.2.7 */ public boolean isObjectReference(Class<?> c) { if (c == null) { throw new IllegalArgumentException(); } return (c.isInterface() && org.omg.CORBA.Object.class.isAssignableFrom(c)); } /** * Section 1.2.8 */ public boolean isEntity(Class<?> c) { if (c == null) { throw new IllegalArgumentException(); } Class<?> superClass = c.getSuperclass(); return (!c.isInterface() && (superClass != null) && (org.omg.CORBA.portable.IDLEntity.class.isAssignableFrom(c))); } /** * Return true if given method is legal property accessor as defined in Section 1.3.4.3 of Java2IDL spec. */ public boolean isPropertyAccessorMethod(Method m, Class<?> c) { String methodName = m.getName(); Class<?> returnType = m.getReturnType(); Class<?>[] parameters = m.getParameterTypes(); String propertyType = null; if (methodName.startsWith(GET_PROPERTY_PREFIX)) { if ((parameters.length == 0) && (returnType != Void.TYPE) && !readHasCorrespondingIsProperty(m, c)) { propertyType = GET_PROPERTY_PREFIX; } } else if (methodName.startsWith(SET_PROPERTY_PREFIX)) { if ((returnType == Void.TYPE) && (parameters.length == 1)) { if (hasCorrespondingReadProperty(m, c, GET_PROPERTY_PREFIX) || hasCorrespondingReadProperty(m, c, IS_PROPERTY_PREFIX)) { propertyType = SET_PROPERTY_PREFIX; } } } else if (methodName.startsWith(IS_PROPERTY_PREFIX)) { if ((parameters.length == 0) && (returnType == Boolean.TYPE) && !isHasCorrespondingReadProperty(m, c)) { propertyType = IS_PROPERTY_PREFIX; } } // Some final checks that apply to all properties. if (propertyType != null) { if (!validPropertyExceptions(m) || (methodName.length() <= propertyType.length())) { propertyType = null; } } return (propertyType != null); } private boolean hasCorrespondingReadProperty(Method writeProperty, Class<?> c, String readPropertyPrefix) { String writePropertyMethodName = writeProperty.getName(); Class<?>[] writePropertyParameters = writeProperty.getParameterTypes(); boolean foundReadProperty = false; try { // Look for a valid corresponding Read property String readPropertyMethodName = writePropertyMethodName.replaceFirst(SET_PROPERTY_PREFIX, readPropertyPrefix); Method readPropertyMethod = c.getMethod(readPropertyMethodName, new Class[]{}); foundReadProperty = (isPropertyAccessorMethod(readPropertyMethod, c) && (readPropertyMethod.getReturnType() == writePropertyParameters[0])); } catch (Exception e) { // ignore. this means we didn't find a corresponding get property. } return foundReadProperty; } @SuppressWarnings("unused") private boolean readHasCorrespondingIsProperty(Method readProperty, Class<?> c) { if (FOLLOW_RMIC) return false; String readPropertyMethodName = readProperty.getName(); boolean foundIsProperty = false; try { // Look for a valid corresponding Is property String isPropertyMethodName = readPropertyMethodName.replaceFirst(GET_PROPERTY_PREFIX, IS_PROPERTY_PREFIX); Method isPropertyMethod = c.getMethod(isPropertyMethodName, new Class[]{}); foundIsProperty = isPropertyAccessorMethod(isPropertyMethod, c); } catch (Exception e) { // ignore. this means we didn't find a corresponding Is property. } return foundIsProperty; } private boolean isHasCorrespondingReadProperty(Method readProperty, Class<?> c) { if (!FOLLOW_RMIC) return false; String readPropertyMethodName = readProperty.getName(); boolean foundIsProperty = false; try { // Look for a valid corresponding Read property String isPropertyMethodName = readPropertyMethodName.replaceFirst(IS_PROPERTY_PREFIX, GET_PROPERTY_PREFIX); Method isPropertyMethod = c.getMethod(isPropertyMethodName, new Class[]{}); foundIsProperty = isPropertyAccessorMethod(isPropertyMethod, c); } catch (Exception e) { // ignore. this means we didn't find a corresponding read property. } return foundIsProperty; } public String getAttributeNameForProperty(String propertyName) { String attributeName = null; String prefix = null; if (propertyName.startsWith(GET_PROPERTY_PREFIX)) { prefix = GET_PROPERTY_PREFIX; } else if (propertyName.startsWith(SET_PROPERTY_PREFIX)) { prefix = SET_PROPERTY_PREFIX; } else if (propertyName.startsWith(IS_PROPERTY_PREFIX)) { prefix = IS_PROPERTY_PREFIX; } if ((prefix != null) && (prefix.length() < propertyName.length())) { String remainder = propertyName.substring(prefix.length()); if ((remainder.length() >= 2) && Character.isUpperCase(remainder.charAt(0)) && Character.isUpperCase(remainder.charAt(1))) { // don't set the first letter to lower-case if the // first two are upper-case attributeName = remainder; } else { attributeName = Character.toLowerCase(remainder.charAt(0)) + remainder.substring(1); } } return attributeName; } /** * Return IDL Type name for primitive types as defined in Section 1.3.3 of Java2IDL spec or null if not a primitive * type. */ public IDLType getPrimitiveIDLTypeMapping(Class<?> c) { if (c == null) { throw new IllegalArgumentException(); } if (c.isPrimitive()) { if (c == Void.TYPE) { return new IDLType(c, "void"); } else if (c == Boolean.TYPE) { return new IDLType(c, "boolean"); } else if (c == Character.TYPE) { return new IDLType(c, "wchar"); } else if (c == Byte.TYPE) { return new IDLType(c, "octet"); } else if (c == Short.TYPE) { return new IDLType(c, "short"); } else if (c == Integer.TYPE) { return new IDLType(c, "long"); } else if (c == Long.TYPE) { return new IDLType(c, "long_long"); } else if (c == Float.TYPE) { return new IDLType(c, "float"); } else if (c == Double.TYPE) { return new IDLType(c, "double"); } } return null; } /** * Return IDL Type name for special case type mappings as defined in Table 1-1 of Java2IDL spec or null if given * class is not a special type. */ public IDLType getSpecialCaseIDLTypeMapping(Class<?> c) { if (c == null) { throw new IllegalArgumentException(); } if (c == java.lang.Object.class) { return new IDLType(c, new String[]{"java", "lang"}, "Object"); } else if (c == java.lang.String.class) { return new IDLType(c, new String[]{"CORBA"}, "WStringValue"); } else if (c == java.lang.Class.class) { return new IDLType(c, new String[]{"javax", "rmi", "CORBA"}, "ClassDesc"); } else if (c == java.io.Serializable.class) { return new IDLType(c, new String[]{"java", "io"}, "Serializable"); } else if (c == java.io.Externalizable.class) { return new IDLType(c, new String[]{"java", "io"}, "Externalizable"); } else if (c == java.rmi.Remote.class) { return new IDLType(c, new String[]{"java", "rmi"}, "Remote"); } else if (c == org.omg.CORBA.Object.class) { return new IDLType(c, "Object"); } else { return null; } } /** * Implements 1.2.3 #2 and #4 */ private void validateExceptions(Method method) throws IDLTypeException { Class<?>[] exceptions = method.getExceptionTypes(); boolean declaresRemoteExceptionOrSuperClass = false; // Section 1.2.3, #2 for (int eIndex = 0; eIndex < exceptions.length; eIndex++) { Class<?> exception = exceptions[eIndex]; if (isRemoteExceptionOrSuperClass(exception)) { declaresRemoteExceptionOrSuperClass = true; break; } } if (!declaresRemoteExceptionOrSuperClass) { String msg = "Method '" + method + "' must throw at least one " + "exception of type java.rmi.RemoteException or one of its " + "super-classes"; throw new IDLTypeException(msg); } // Section 1.2.3, #4 // See also bug 4972402 // For all exceptions E in exceptions, // (isCheckedException(E) => (isValue(E) || RemoteException.isAssignableFrom( E ) ) for (int eIndex = 0; eIndex < exceptions.length; eIndex++) { Class<?> exception = exceptions[eIndex]; if (isCheckedException(exception) && !isValue(exception) && !isRemoteException(exception)) { String msg = "Exception '" + exception + "' on method '" + method + "' is not a allowed RMI/IIOP exception type"; throw new IDLTypeException(msg); } } return; } /** * Returns true if the method's throw clause conforms to the exception restrictions for properties as defined in * Section 1.3.4.3 of Java2IDL spec. This means that for all exceptions E declared on the method, E isChecked => * RemoteException.isAssignableFrom( E ). */ private boolean validPropertyExceptions(Method method) { Class<?>[] exceptions = method.getExceptionTypes(); for (int eIndex = 0; eIndex < exceptions.length; eIndex++) { Class<?> exception = exceptions[eIndex]; if (isCheckedException(exception) && !isRemoteException(exception)) return false; } return true; } /** * Implements Section 1.2.3, #2. */ private boolean isRemoteExceptionOrSuperClass(Class<?> c) { return ((c == java.rmi.RemoteException.class) || (c == java.io.IOException.class) || (c == java.lang.Exception.class) || (c == java.lang.Throwable.class)); } /** * Implements 1.2.3 #6 */ private void validateConstants(final Class<?> c) throws IDLTypeException { Field[] fields = null; try { fields = java.security.AccessController.doPrivileged(new java.security.PrivilegedExceptionAction<Field[]>() { public Field[] run() throws Exception { return c.getFields(); } }); } catch (java.security.PrivilegedActionException pae) { IDLTypeException ite = new IDLTypeException(); ite.initCause(pae); throw ite; } for (int i = 0; i < fields.length; i++) { Field next = fields[i]; Class<?> fieldType = next.getType(); if ((fieldType != java.lang.String.class) && !isPrimitive(fieldType)) { String msg = "Constant field '" + next.getName() + "' in class '" + next.getDeclaringClass().getName() + "' has invalid type' " + next.getType() + "'. Constants" + " in RMI/IIOP interfaces can only have primitive" + " types and java.lang.String types."; throw new IDLTypeException(msg); } } return; } }