/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.axis2.jaxws.description.builder; import org.apache.axis2.java.security.AccessController; import org.apache.axis2.jaxws.ExceptionFactory; import org.apache.axis2.jaxws.i18n.Messages; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import javax.xml.namespace.QName; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; /** * */ class DescriptionBuilderUtils { private static final Log log = LogFactory.getLog(DescriptionBuilderUtils.class); static String JAXWS_HOLDER_CLASS = "javax.xml.ws.Holder"; private static final String INT_PRIMITIVE = "int"; private static final String INT_PRIMITIVE_ENCODING = "I"; private static final String BYTE_PRIMITIVE = "byte"; private static final String BYTE_PRIMITIVE_ENCODING = "B"; private static final String CHAR_PRIMITIVE = "char"; private static final String CHAR_PRIMITIVE_ENCODING = "C"; private static final String SHORT_PRIMITIVE = "short"; private static final String SHORT_PRIMITIVE_ENCODING = "S"; private static final String BOOLEAN_PRIMITIVE = "boolean"; private static final String BOOLEAN_PRIMITIVE_ENCODING = "Z"; private static final String LONG_PRIMITIVE = "long"; private static final String LONG_PRIMITIVE_ENCODING = "J"; private static final String FLOAT_PRIMITIVE = "float"; private static final String FLOAT_PRIMITIVE_ENCODING = "F"; private static final String DOUBLE_PRIMITIVE = "double"; private static final String DOUBLE_PRIMITIVE_ENCODING = "D"; private static final String VOID_PRIMITIVE = "void"; // REVIEW: This may not be the correct encoding for Void private static final String VOID_PRIMITIVE_ENCODING = "V"; /** * Returns a string representing the outermost generic raw type class, or null if the argument * is not a generic. For example if the string "javax.xml.ws.Holder<my.package.MyObject>" is * passed in, the string "javax.xml.ws.Holder" will be returned. * <p/> * Note that generic arrays are supported. For example, for "Holder<List<String>[][]", the * returned value will be "List[][]". * * @param inputType * @return A string representing the generic raw type or null if there is no generic. */ static String getRawType(String inputType) { String returnRawType = null; int leftBracket = inputType.indexOf("<"); int rightBracket = inputType.lastIndexOf(">"); if (leftBracket > 0 && rightBracket > 0 && rightBracket > leftBracket) { String part1 = inputType.substring(0, leftBracket); if ((rightBracket + 1) == inputType.length()) { // There is nothing after the closing ">" we need to append to the raw type returnRawType = part1; } else { // Skip over the closing ">" then append the rest of the string to the raw type // This would be an array declaration for example. String part2 = inputType.substring(rightBracket + 1).trim(); returnRawType = part1 + part2; } } return returnRawType; } /** * Return the actual type in a JAX-WS holder declaration. For example, for the argument * "javax.xml.ws.Holder<my.package.MyObject>", return "my.package.MyObject". If the actual type * itself is a generic, then that raw type will be returned. For example, * "javax.xml.ws.Holder<java.util.List<my.package.MyObject>>" will return "java.util.List". * <p/> * Note that Holders of Arrays and of Generic Arrays are also supported. For example, for * "javax.xml.ws.Holder<String[]>", return "String[]". For an array of a generic, the array of * the raw type is returned. For example, for "javax.xml.ws.Holder<List<String>[][]>", return * "List[][]". * <p/> * Important note! The JAX-WS Holder generic only supports a single actual type, i.e. the * generic is javax.xml.ws.Holder<T>. This method is not general purpose; it does not support * generics with multiple types such as Generic<K,V> at the outermost level. * * @param holderInputString * @return return the actual argument class name for a JAX-WS Holder; returns null if the * argument is not a JAX-WS Holder */ static String getHolderActualType(String holderInputString) { String returnString = null; if (DescriptionBuilderUtils.isHolderType(holderInputString)) { int leftBracket = holderInputString.indexOf("<"); int rightBracket = holderInputString.lastIndexOf(">"); if (leftBracket > 0 && rightBracket > leftBracket + 1) { // Get everything between the outermost "<" and ">" String actualType = holderInputString.substring(leftBracket + 1, rightBracket).trim(); // If the holder contained a generic, then get the generic raw type (e.g. "List" for // Holder<List<String>>). String rawType = getRawType(actualType); if (rawType != null) { returnString = rawType; } else { return returnString = actualType; } } } return returnString; } /** * Check if the input String is a JAX-WS Holder. For example "javax.xml.ws.Holder<my.package.MyObject>". * * @param checkType * @return true if it is a JAX-WS Holder type; false otherwise. */ static boolean isHolderType(String checkType) { boolean isHolder = false; if (checkType != null) { if (checkType.startsWith(JAXWS_HOLDER_CLASS)) { isHolder = checkType.length() == JAXWS_HOLDER_CLASS.length() || checkType.charAt(JAXWS_HOLDER_CLASS.length()) == '<'; } } return isHolder; } /** * Answers if the String representing the class contains an array declaration. For example * "Foo[][]" would return true, as would "int[]". * * @param className * @return */ static boolean isClassAnArray(String className) { if (className != null && className.indexOf("[") > 0) { return true; } else { return false; } } /** * For an class name that is an array, return the non-array declaration portion. For example * "my.package.Foo[][]" would return "my.package.Foo". Returns null if the argument does not * contain an array declaration. * * @param fullClassName * @return */ static String getBaseArrayClassName(String fullClassName) { String baseArrayClassName = null; if (fullClassName != null) { int firstArrayDimension = fullClassName.indexOf("["); if (firstArrayDimension > 0) { baseArrayClassName = fullClassName.substring(0, firstArrayDimension); } } return baseArrayClassName; } /** * Return a prefix suitable for passing to Class.forName(String) for an array. Each array * dimension represented by "[]" will be represented by a single "[". * * @param arrayClassName * @return */ static String getArrayDimensionPrefix(String arrayClassName) { StringBuffer arrayDimPrefix = new StringBuffer(); if (arrayClassName != null) { int arrayDimIndex = arrayClassName.indexOf("[]"); while (arrayDimIndex > 0) { arrayDimPrefix.append("["); // Skip over this "[]" and see if there are any more. int startNext = arrayDimIndex + 2; arrayDimIndex = arrayClassName.indexOf("[]", startNext); } } if (arrayDimPrefix.length() > 0) return arrayDimPrefix.toString(); else return null; } /** * For primitives, return the appropriate primitive class. Note that arrays of primitives are * handled differently, like arrays of objects. Only non-array primitives are processed by this * method. This method understands both the typical primitive declaration (e.g. "int") and the * encoding used as for arrays (e.g. "I"). * * @param classType * @return */ static Class getPrimitiveClass(String classType) { Class paramClass = null; if (INT_PRIMITIVE.equals(classType) || INT_PRIMITIVE_ENCODING.equals(classType)) { paramClass = int.class; } else if (BYTE_PRIMITIVE.equals(classType) || BYTE_PRIMITIVE_ENCODING.equals(classType)) { paramClass = byte.class; } else if (CHAR_PRIMITIVE.equals(classType) || CHAR_PRIMITIVE_ENCODING.equals(classType)) { paramClass = char.class; } else if (SHORT_PRIMITIVE.equals(classType) || SHORT_PRIMITIVE_ENCODING.equals(classType)) { paramClass = short.class; } else if (BOOLEAN_PRIMITIVE.equals(classType) || BOOLEAN_PRIMITIVE_ENCODING.equals(classType)) { paramClass = boolean.class; } else if (LONG_PRIMITIVE.equals(classType) || LONG_PRIMITIVE_ENCODING.equals(classType)) { paramClass = long.class; } else if (FLOAT_PRIMITIVE.equals(classType) || FLOAT_PRIMITIVE_ENCODING.equals(classType)) { paramClass = float.class; } else if (DOUBLE_PRIMITIVE.equals(classType) || DOUBLE_PRIMITIVE_ENCODING.equals(classType)) { paramClass = double.class; } else if (VOID_PRIMITIVE.equals(classType) || VOID_PRIMITIVE_ENCODING.equals(classType)) { paramClass = void.class; } return paramClass; } /** * Returns the encoding used to represent a Class for an array of a primitive type. For * example, an array of boolean is represented by "Z". This is as described in the javadoc for * Class.getName(). If the argument is not a primitive type, a null will be returned. * <p/> * Note that arrays of voids are not allowed; a null will be returned. * * @param primitiveType * @return */ static String getPrimitiveTypeArrayEncoding(String primitiveType) { String encoding = null; if (BOOLEAN_PRIMITIVE.equals(primitiveType)) { encoding = BOOLEAN_PRIMITIVE_ENCODING; } else if (BYTE_PRIMITIVE.equals(primitiveType)) { encoding = BYTE_PRIMITIVE_ENCODING; } else if (CHAR_PRIMITIVE.equals(primitiveType)) { encoding = CHAR_PRIMITIVE_ENCODING; } else if (DOUBLE_PRIMITIVE.equals(primitiveType)) { encoding = DOUBLE_PRIMITIVE_ENCODING; } else if (FLOAT_PRIMITIVE.equals(primitiveType)) { encoding = FLOAT_PRIMITIVE_ENCODING; } else if (INT_PRIMITIVE.equals(primitiveType)) { encoding = INT_PRIMITIVE_ENCODING; } else if (LONG_PRIMITIVE.equals(primitiveType)) { encoding = LONG_PRIMITIVE_ENCODING; } else if (SHORT_PRIMITIVE.equals(primitiveType)) { encoding = SHORT_PRIMITIVE_ENCODING; } return encoding; } /** * If the parameter represents and array, then the returned string is in a format that a * Class.forName(String) can be done on it. This format is described by Class.getName(). If the * parameter does not represent an array, the parememter is returned unmodified. * <p/> * Note that arrays of primitives are processed as well as arrays of objects. * * @param classToLoad * @return */ static String reparseIfArray(String classToLoad) { if (log.isDebugEnabled()) { log.debug("entry with String parameter classToLoad: " + classToLoad); } if (classToLoad.startsWith("[")) { // It appears that the string is already in binary form. // Detect if the form is valid and fix it if it is not. // For example, sometimes a [my.Foo is input instead of the required [Lmy.Foo; String binaryForm = classToLoad; int indexAfterBracket = classToLoad.lastIndexOf("[") + 1; String base = classToLoad.substring(indexAfterBracket); String dims = classToLoad.substring(0, indexAfterBracket); if (getPrimitiveClass(base) == null) { // Make sure the base starts with an L and ends with a ; if (!base.startsWith("L") && !base.endsWith(";")) { binaryForm = dims + "L" + base + ";"; } } if (log.isDebugEnabled()) { log.debug("exit method with String return value binaryForm: " + binaryForm); } return binaryForm; } String reparsedClassName = classToLoad; if (isClassAnArray(classToLoad)) { String baseType = getBaseArrayClassName(classToLoad); String dimensionPrefix = getArrayDimensionPrefix(classToLoad); if (getPrimitiveTypeArrayEncoding(baseType) != null) { reparsedClassName = dimensionPrefix + getPrimitiveTypeArrayEncoding(baseType); } else { reparsedClassName = dimensionPrefix + "L" + baseType + ";"; } } if (log.isDebugEnabled()) { log.debug("exit method with String return value reparsedClassName: " + reparsedClassName); } return reparsedClassName; } /** * Load a class represented in a Description Builder Composite. If a classloader is specified, * it will be used; otherwise the default classloader is used. * * @param classToLoad * @param classLoader * @return */ static Class loadClassFromComposite(String classToLoad, ClassLoader classLoader) { Class returnClass = null; // If this is an array, then create a string version as described by Class.getName. // For example, "Foo[][]" becomes "[[LFoo". Note that arrays of primitives must also be parsed. classToLoad = DescriptionBuilderUtils.reparseIfArray(classToLoad); if (classLoader != null) { // Use the specified classloader to load the class. try { returnClass = forName(classToLoad, false, classLoader); } //Catch Throwable as ClassLoader can throw an NoClassDefFoundError that //does not extend Exception, so lets catch everything that extends Throwable //rather than just Exception. catch (Throwable ex) { throw ExceptionFactory.makeWebServiceException( Messages.getMessage("DBUClassNotFound", classToLoad, classLoader.toString())); } } else { //Use the thread context class loader to load the class. try { returnClass = forName(classToLoad, false, getContextClassLoader(null)); } catch (Throwable ex) { //Use the default classloader to load the class. try { returnClass = forName(classToLoad); } //Catch Throwable as ClassLoader can throw an NoClassDefFoundError that //does not extend Exception catch (Throwable ex2) { throw ExceptionFactory.makeWebServiceException( Messages.getMessage("DBUClassNotFound2", classToLoad)); } } } return returnClass; } static boolean isEmpty(String string) { return (string == null || "".equals(string)); } static boolean isEmpty(QName qname) { return qname == null || isEmpty(qname.getLocalPart()); } /** * Return the class for this name * * @return Class */ private static Class forName(final String className, final boolean initialize, final ClassLoader classloader) throws ClassNotFoundException { Class cl = null; try { cl = (Class) AccessController.doPrivileged( new PrivilegedExceptionAction() { public Object run() throws ClassNotFoundException { return Class.forName(className, initialize, classloader); } } ); } catch (PrivilegedActionException e) { if (log.isDebugEnabled()) { log.debug("Exception thrown from AccessController: " + e.getMessage(), e); } throw (ClassNotFoundException) e.getException(); } return cl; } /** * Return the class for this name * * @return Class */ private static Class forName(final String className) throws ClassNotFoundException { Class cl = null; try { cl = (Class) AccessController.doPrivileged( new PrivilegedExceptionAction() { public Object run() throws ClassNotFoundException { return Class.forName(className); } } ); } catch (PrivilegedActionException e) { if (log.isDebugEnabled()) { log.debug("Exception thrown from AccessController: " + e.getMessage(), e); } throw (ClassNotFoundException) e.getException(); } return cl; } /** * @return ClassLoader */ private static ClassLoader getContextClassLoader(final ClassLoader classLoader) { ClassLoader cl; try { cl = (ClassLoader) AccessController.doPrivileged( new PrivilegedExceptionAction() { public Object run() throws ClassNotFoundException { return classLoader != null ? classLoader : Thread.currentThread().getContextClassLoader(); } } ); } catch (PrivilegedActionException e) { if (log.isDebugEnabled()) { log.debug("Exception thrown from AccessController: " + e.getMessage(), e); } throw ExceptionFactory.makeWebServiceException(e.getException()); } return cl; } }