1 /* 2 * Copyright (c) 1997, 2014, 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 package com.sun.xml.internal.ws.policy.privateutil; 27 28 import com.sun.xml.internal.ws.policy.PolicyException; 29 import java.io.Closeable; 30 import java.io.IOException; 31 import java.io.UnsupportedEncodingException; 32 import java.lang.reflect.InvocationTargetException; 33 import java.lang.reflect.Method; 34 import java.net.URL; 35 import java.util.ArrayList; 36 import java.util.Arrays; 37 import java.util.Collection; 38 import java.util.Comparator; 39 import java.util.LinkedList; 40 import java.util.List; 41 import java.util.Queue; 42 import java.util.logging.Level; 43 import javax.xml.namespace.QName; 44 import javax.xml.stream.XMLStreamException; 45 import javax.xml.stream.XMLStreamReader; 46 47 /** 48 * This is a wrapper class for various utilities that may be reused within Policy API implementation. 49 * The class is not part of public Policy API. Do not use it from your client code! 50 * 51 * @author Marek Potociar 52 */ 53 public final class PolicyUtils { 54 private PolicyUtils() { } 55 56 public static class Commons { 57 /** 58 * Method returns the name of the method that is on the {@code methodIndexInStack} 59 * position in the call stack of the current {@link Thread}. 60 * 61 * @param methodIndexInStack index to the call stack to get the method name for. 62 * @return the name of the method that is on the {@code methodIndexInStack} 63 * position in the call stack of the current {@link Thread}. 64 */ 65 public static String getStackMethodName(final int methodIndexInStack) { 66 final String methodName; 67 68 final StackTraceElement[] stack = Thread.currentThread().getStackTrace(); 69 if (stack.length > methodIndexInStack + 1) { 70 methodName = stack[methodIndexInStack].getMethodName(); 71 } else { 72 methodName = "UNKNOWN METHOD"; 73 } 74 75 return methodName; 76 } 77 78 /** 79 * Function returns the name of the caller method for the method executing this 80 * function. 81 * 82 * @return caller method name from the call stack of the current {@link Thread}. 83 */ 84 public static String getCallerMethodName() { 85 String result = getStackMethodName(5); 86 if (result.equals("invoke0")) { 87 // We are likely running on Mac OS X, which returns a shorter stack trace 88 result = getStackMethodName(4); 89 } 90 return result; 91 } 92 } 93 94 public static class IO { 95 private static final PolicyLogger LOGGER = PolicyLogger.getLogger(PolicyUtils.IO.class); 96 97 /** 98 * If the {@code resource} is not {@code null}, this method will try to close the 99 * {@code resource} instance and log warning about any unexpected 100 * {@link IOException} that may occur. 101 * 102 * @param resource resource to be closed 103 */ 104 public static void closeResource(Closeable resource) { 105 if (resource != null) { 106 try { 107 resource.close(); 108 } catch (IOException e) { 109 LOGGER.warning(LocalizationMessages.WSP_0023_UNEXPECTED_ERROR_WHILE_CLOSING_RESOURCE(resource.toString()), e); 110 } 111 } 112 } 113 114 /** 115 * If the {@code reader} is not {@code null}, this method will try to close the 116 * {@code reader} instance and log warning about any unexpected 117 * {@link IOException} that may occur. 118 * 119 * @param reader resource to be closed 120 */ 121 public static void closeResource(XMLStreamReader reader) { 122 if (reader != null) { 123 try { 124 reader.close(); 125 } catch (XMLStreamException e) { 126 LOGGER.warning(LocalizationMessages.WSP_0023_UNEXPECTED_ERROR_WHILE_CLOSING_RESOURCE(reader.toString()), e); 127 } 128 } 129 } 130 } 131 132 /** 133 * Text utilities wrapper. 134 */ 135 public static class Text { 136 /** 137 * System-specific line separator character retrieved from the Java system property 138 * <code>line.separator</code> 139 */ 140 public final static String NEW_LINE = System.getProperty("line.separator"); 141 142 /** 143 * Method creates indent string consisting of as many {@code TAB} characters as specified by {@code indentLevel} parameter 144 * 145 * @param indentLevel indentation level 146 * @return indentation string as specified by indentation level 147 * 148 */ 149 public static String createIndent(final int indentLevel) { 150 final char[] charData = new char[indentLevel * 4]; 151 Arrays.fill(charData, ' '); 152 return String.valueOf(charData); 153 } 154 } 155 156 public static class Comparison { 157 /** 158 * The comparator comapres QName objects according to their publicly accessible attributes, in the following 159 * order of attributes: 160 * 161 * 1. namespace (not null String) 162 * 2. local name (not null String) 163 */ 164 public static final Comparator<QName> QNAME_COMPARATOR = new Comparator<QName>() { 165 public int compare(final QName qn1, final QName qn2) { 166 if (qn1 == qn2 || qn1.equals(qn2)) { 167 return 0; 168 } 169 170 int result; 171 172 result = qn1.getNamespaceURI().compareTo(qn2.getNamespaceURI()); 173 if (result != 0) { 174 return result; 175 } 176 177 return qn1.getLocalPart().compareTo(qn2.getLocalPart()); 178 } 179 }; 180 181 /** 182 * Compares two boolean values in the following way: {@code false < true} 183 * 184 * @return {@code -1} if {@code b1 < b2}, {@code 0} if {@code b1 == b2}, {@code 1} if {@code b1 > b2} 185 */ 186 public static int compareBoolean(final boolean b1, final boolean b2) { 187 final int i1 = (b1) ? 1 : 0; 188 final int i2 = (b2) ? 1 : 0; 189 190 return i1 - i2; 191 } 192 193 /** 194 * Compares two String values, that may possibly be null in the following way: {@code null < "string value"} 195 * 196 * @return {@code -1} if {@code s1 < s2}, {@code 0} if {@code s1 == s2}, {@code 1} if {@code s1 > s2} 197 */ 198 public static int compareNullableStrings(final String s1, final String s2) { 199 return ((s1 == null) ? ((s2 == null) ? 0 : -1) : ((s2 == null) ? 1 : s1.compareTo(s2))); 200 } 201 } 202 203 public static class Collections { 204 private static final PolicyLogger LOGGER = PolicyLogger.getLogger(PolicyUtils.Collections.class); 205 /** 206 * TODO javadocs 207 * 208 * @param initialBase the combination base that will be present in each combination. May be {@code null} or empty. 209 * @param options options that should be combined. May be {@code null} or empty. 210 * @param ignoreEmptyOption flag identifies whether empty options should be ignored or whether the method should halt 211 * processing and return {@code null} when an empty option is encountered 212 * @return TODO 213 */ 214 public static <E, T extends Collection<? extends E>, U extends Collection<? extends E>> Collection<Collection<E>> combine(final U initialBase, final Collection<T> options, final boolean ignoreEmptyOption) { 215 List<Collection<E>> combinations = null; 216 if (options == null || options.isEmpty()) { 217 // no combination creation needed 218 if (initialBase != null) { 219 combinations = new ArrayList<Collection<E>>(1); 220 combinations.add(new ArrayList<E>(initialBase)); 221 } 222 return combinations; 223 } 224 225 // creating defensive and modifiable copy of the base 226 final Collection<E> base = new LinkedList<E>(); 227 if (initialBase != null && !initialBase.isEmpty()) { 228 base.addAll(initialBase); 229 } 230 /** 231 * now we iterate over all options and build up an option processing queue: 232 * 1. if ignoreEmptyOption flag is not set and we found an empty option, we are going to stop processing and return null. Otherwise we 233 * ignore the empty option. 234 * 2. if the option has one child only, we add the child directly to the base. 235 * 3. if there are more children in examined node, we add it to the queue for further processing and precoumpute the final size of 236 * resulting collection of combinations. 237 */ 238 int finalCombinationsSize = 1; 239 final Queue<T> optionProcessingQueue = new LinkedList<T>(); 240 for (T option : options) { 241 final int optionSize = option.size(); 242 243 if (optionSize == 0) { 244 if (!ignoreEmptyOption) { 245 return null; 246 } 247 } else if (optionSize == 1) { 248 base.addAll(option); 249 } else { 250 boolean entered = optionProcessingQueue.offer(option); 251 if (!entered) { 252 throw LOGGER.logException(new RuntimePolicyUtilsException(LocalizationMessages.WSP_0096_ERROR_WHILE_COMBINE(option)), false, Level.WARNING); 253 } 254 finalCombinationsSize *= optionSize; 255 } 256 } 257 258 // creating final combinations 259 combinations = new ArrayList<Collection<E>>(finalCombinationsSize); 260 combinations.add(base); 261 if (finalCombinationsSize > 1) { 262 T processedOption; 263 while ((processedOption = optionProcessingQueue.poll()) != null) { 264 final int actualSemiCombinationCollectionSize = combinations.size(); 265 final int newSemiCombinationCollectionSize = actualSemiCombinationCollectionSize * processedOption.size(); 266 267 int semiCombinationIndex = 0; 268 for (E optionElement : processedOption) { 269 for (int i = 0; i < actualSemiCombinationCollectionSize; i++) { 270 final Collection<E> semiCombination = combinations.get(semiCombinationIndex); // unfinished combination 271 272 if (semiCombinationIndex + actualSemiCombinationCollectionSize < newSemiCombinationCollectionSize) { 273 // this is not the last optionElement => we create a new combination copy for the next child 274 combinations.add(new LinkedList<E>(semiCombination)); 275 } 276 277 semiCombination.add(optionElement); 278 semiCombinationIndex++; 279 } 280 } 281 } 282 } 283 return combinations; 284 } 285 } 286 287 /** 288 * Reflection utilities wrapper 289 */ 290 static class Reflection { 291 private static final PolicyLogger LOGGER = PolicyLogger.getLogger(PolicyUtils.Reflection.class); 292 293 /** 294 * Reflectively invokes specified method on the specified target 295 */ 296 static <T> T invoke(final Object target, final String methodName, 297 final Class<T> resultClass, final Object... parameters) throws RuntimePolicyUtilsException { 298 Class[] parameterTypes; 299 if (parameters != null && parameters.length > 0) { 300 parameterTypes = new Class[parameters.length]; 301 int i = 0; 302 for (Object parameter : parameters) { 303 parameterTypes[i++] = parameter.getClass(); 304 } 305 } else { 306 parameterTypes = null; 307 } 308 309 return invoke(target, methodName, resultClass, parameters, parameterTypes); 310 } 311 312 /** 313 * Reflectively invokes specified method on the specified target 314 */ 315 public static <T> T invoke(final Object target, final String methodName, final Class<T> resultClass, 316 final Object[] parameters, final Class[] parameterTypes) throws RuntimePolicyUtilsException { 317 try { 318 final Method method = target.getClass().getMethod(methodName, parameterTypes); 319 final Object result = MethodUtil.invoke(target, method,parameters); 320 321 return resultClass.cast(result); 322 } catch (IllegalArgumentException e) { 323 throw LOGGER.logSevereException(new RuntimePolicyUtilsException(createExceptionMessage(target, parameters, methodName), e)); 324 } catch (InvocationTargetException e) { 325 throw LOGGER.logSevereException(new RuntimePolicyUtilsException(createExceptionMessage(target, parameters, methodName), e)); 326 } catch (IllegalAccessException e) { 327 throw LOGGER.logSevereException(new RuntimePolicyUtilsException(createExceptionMessage(target, parameters, methodName), e.getCause())); 328 } catch (SecurityException e) { 329 throw LOGGER.logSevereException(new RuntimePolicyUtilsException(createExceptionMessage(target, parameters, methodName), e)); 330 } catch (NoSuchMethodException e) { 331 throw LOGGER.logSevereException(new RuntimePolicyUtilsException(createExceptionMessage(target, parameters, methodName), e)); 332 } 333 } 334 335 private static String createExceptionMessage(final Object target, final Object[] parameters, final String methodName) { 336 return LocalizationMessages.WSP_0061_METHOD_INVOCATION_FAILED(target.getClass().getName(), methodName, 337 parameters == null ? null : Arrays.asList(parameters).toString()); 338 } 339 } 340 341 public static class ConfigFile { 342 /** 343 * Generates a config file resource name from provided config file identifier. 344 * The generated file name can be transformed into a URL instance using 345 * {@link #loadFromContext(String, Object)} or {@link #loadFromClasspath(String)} 346 * method. 347 * 348 * @param configFileIdentifier the string used to generate the config file URL that will be parsed. Each WSIT config 349 * file is in form of <code>wsit-<i>{configFileIdentifier}</i>.xml</code>. Must not be {@code null}. 350 * @return generated config file resource name 351 * @throw PolicyException If configFileIdentifier is null. 352 */ 353 public static String generateFullName(final String configFileIdentifier) throws PolicyException { 354 if (configFileIdentifier != null) { 355 final StringBuffer buffer = new StringBuffer("wsit-"); 356 buffer.append(configFileIdentifier).append(".xml"); 357 return buffer.toString(); 358 } else { 359 throw new PolicyException(LocalizationMessages.WSP_0080_IMPLEMENTATION_EXPECTED_NOT_NULL()); 360 } 361 } 362 363 /** 364 * Returns a URL pointing to the given config file. The file name is 365 * looked up as a resource from a ServletContext. 366 * 367 * May return null if the file can not be found. 368 * 369 * @param configFileName The name of the file resource 370 * @param context A ServletContext object. May not be null. 371 */ 372 public static URL loadFromContext(final String configFileName, final Object context) { 373 return Reflection.invoke(context, "getResource", URL.class, configFileName); 374 } 375 376 /** 377 * Returns a URL pointing to the given config file. The file is looked up as 378 * a resource on the classpath. 379 * 380 * May return null if the file can not be found. 381 * 382 * @param configFileName the name of the file resource. May not be {@code null}. 383 */ 384 public static URL loadFromClasspath(final String configFileName) { 385 final ClassLoader cl = Thread.currentThread().getContextClassLoader(); 386 if (cl == null) { 387 return ClassLoader.getSystemResource(configFileName); 388 } else { 389 return cl.getResource(configFileName); 390 } 391 } 392 } 393 394 /** 395 * Wrapper for ServiceFinder class which is not part of the Java SE yet. 396 */ 397 public static class ServiceProvider { 398 /** 399 * Locates and incrementally instantiates the available providers of a 400 * given service using the given class loader. 401 * <p/> 402 * <p> This method transforms the name of the given service class into a 403 * provider-configuration filename as described above and then uses the 404 * {@code getResources} method of the given class loader to find all 405 * available files with that name. These files are then read and parsed to 406 * produce a list of provider-class names. Eventually each provider class is 407 * instantiated and array of those instances is returned. 408 * <p/> 409 * <p> Because it is possible for extensions to be installed into a running 410 * Java virtual machine, this method may return different results each time 411 * it is invoked. <p> 412 * 413 * @param serviceClass The service's abstract service class. Must not be {@code null}. 414 * @param loader The class loader to be used to load provider-configuration files 415 * and instantiate provider classes, or {@code null} if the system 416 * class loader (or, failing that the bootstrap class loader) is to 417 * be used 418 * @throws NullPointerException in case {@code service} input parameter is {@code null}. 419 * @throws ServiceConfigurationError If a provider-configuration file violates the specified format 420 * or names a provider class that cannot be found and instantiated 421 * @see #load(Class) 422 */ 423 public static <T> T[] load(final Class<T> serviceClass, final ClassLoader loader) { 424 return ServiceFinder.find(serviceClass, loader).toArray(); 425 } 426 427 /** 428 * Locates and incrementally instantiates the available providers of a 429 * given service using the context class loader. This convenience method 430 * is equivalent to 431 * <p/> 432 * <pre> 433 * ClassLoader cl = Thread.currentThread().getContextClassLoader(); 434 * return PolicyUtils.ServiceProvider.load(service, cl); 435 * </pre> 436 * 437 * @param serviceClass The service's abstract service class. Must not be {@code null}. 438 * 439 * @throws NullPointerException in case {@code service} input parameter is {@code null}. 440 * @throws ServiceConfigurationError If a provider-configuration file violates the specified format 441 * or names a provider class that cannot be found and instantiated 442 * @see #load(Class, ClassLoader) 443 */ 444 public static <T> T[] load(final Class<T> serviceClass) { 445 return ServiceFinder.find(serviceClass).toArray(); 446 } 447 } 448 449 public static class Rfc2396 { 450 451 private static final PolicyLogger LOGGER = PolicyLogger.getLogger(PolicyUtils.Reflection.class); 452 453 // converts "hello%20world" into "hello world" 454 public static String unquote(final String quoted) { 455 if (null == quoted) { 456 return null; 457 } 458 final byte[] unquoted = new byte[quoted.length()]; // result cannot be longer than original string 459 int newLength = 0; 460 char c; 461 int hi, lo; 462 for (int i=0; i < quoted.length(); i++) { // iterarate over all chars in the input 463 c = quoted.charAt(i); 464 if ('%' == c) { // next escape sequence found 465 if ((i + 2) >= quoted.length()) { 466 throw LOGGER.logSevereException(new RuntimePolicyUtilsException(LocalizationMessages.WSP_0079_ERROR_WHILE_RFC_2396_UNESCAPING(quoted)), false); 467 } 468 hi = Character.digit(quoted.charAt(++i), 16); 469 lo = Character.digit(quoted.charAt(++i), 16); 470 if ((0 > hi) || (0 > lo)) { 471 throw LOGGER.logSevereException(new RuntimePolicyUtilsException(LocalizationMessages.WSP_0079_ERROR_WHILE_RFC_2396_UNESCAPING(quoted)), false); 472 } 473 unquoted[newLength++] = (byte) (hi * 16 + lo); 474 } else { // regular character found 475 unquoted[newLength++] = (byte) c; 476 } 477 } 478 try { 479 return new String(unquoted, 0, newLength, "utf-8"); 480 } catch (UnsupportedEncodingException uee) { 481 throw LOGGER.logSevereException(new RuntimePolicyUtilsException(LocalizationMessages.WSP_0079_ERROR_WHILE_RFC_2396_UNESCAPING(quoted), uee)); 482 } 483 } 484 } 485 }