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 26package com.sun.xml.internal.ws.binding; 27 28import com.sun.istack.internal.NotNull; 29import com.sun.istack.internal.Nullable; 30import com.sun.xml.internal.ws.api.BindingID; 31import com.sun.xml.internal.ws.api.FeatureListValidator; 32import com.sun.xml.internal.ws.api.FeatureListValidatorAnnotation; 33import com.sun.xml.internal.ws.api.ImpliesWebServiceFeature; 34import com.sun.xml.internal.ws.api.SOAPVersion; 35import com.sun.xml.internal.ws.api.WSBinding; 36import com.sun.xml.internal.ws.api.WSFeatureList; 37import com.sun.xml.internal.ws.api.FeatureConstructor; 38import com.sun.xml.internal.ws.api.model.wsdl.WSDLPort; 39import com.sun.xml.internal.ws.api.model.wsdl.WSDLFeaturedObject; 40import com.sun.xml.internal.ws.model.RuntimeModelerException; 41import com.sun.xml.internal.ws.resources.ModelerMessages; 42import com.sun.xml.internal.bind.util.Which; 43 44import javax.xml.ws.RespectBinding; 45import javax.xml.ws.RespectBindingFeature; 46import javax.xml.ws.WebServiceException; 47import javax.xml.ws.WebServiceFeature; 48import javax.xml.ws.soap.Addressing; 49import javax.xml.ws.soap.AddressingFeature; 50import javax.xml.ws.soap.MTOM; 51import javax.xml.ws.soap.MTOMFeature; 52import javax.xml.ws.spi.WebServiceFeatureAnnotation; 53 54import com.oracle.webservices.internal.api.EnvelopeStyleFeature; 55 56import java.lang.annotation.Annotation; 57import java.lang.reflect.InvocationTargetException; 58import java.lang.reflect.Method; 59import java.lang.reflect.Constructor; 60import java.util.*; 61import java.util.logging.Level; 62import java.util.logging.Logger; 63 64/** 65 * Represents a list of {@link WebServiceFeature}s that has bunch of utility 66 * methods pertaining to web service features. 67 * 68 * @author Rama Pulavarthi 69 */ 70public final class WebServiceFeatureList extends AbstractMap<Class<? extends WebServiceFeature>, WebServiceFeature> implements WSFeatureList { 71 public static WebServiceFeatureList toList(Iterable<WebServiceFeature> features) { 72 if (features instanceof WebServiceFeatureList) 73 return (WebServiceFeatureList) features; 74 WebServiceFeatureList w = new WebServiceFeatureList(); 75 if (features != null) 76 w.addAll(features); 77 return w; 78 } 79 80 private Map<Class<? extends WebServiceFeature>, WebServiceFeature> wsfeatures = new HashMap<Class<? extends WebServiceFeature>, WebServiceFeature>(); 81 private boolean isValidating = false; 82 83 public WebServiceFeatureList() { 84 } 85 86 /** 87 * Delegate to this parent if non-null. 88 */ 89 private @Nullable 90 WSDLFeaturedObject parent; 91 92 public WebServiceFeatureList(@NotNull WebServiceFeature... features) { 93 if (features != null) { 94 for (WebServiceFeature f : features) { 95 addNoValidate(f); 96 } 97 } 98 } 99 100 public void validate() { 101 if (!isValidating) { 102 isValidating = true; 103 104 // validation 105 for (WebServiceFeature ff : this) { 106 validate(ff); 107 } 108 } 109 } 110 111 private void validate(WebServiceFeature feature) { 112 // run validation 113 FeatureListValidatorAnnotation fva = feature.getClass().getAnnotation(FeatureListValidatorAnnotation.class); 114 if (fva != null) { 115 Class<? extends FeatureListValidator> beanClass = fva.bean(); 116 try { 117 FeatureListValidator validator = beanClass.newInstance(); 118 validator.validate(this); 119 } catch (InstantiationException e) { 120 throw new WebServiceException(e); 121 } catch (IllegalAccessException e) { 122 throw new WebServiceException(e); 123 } 124 } 125 } 126 127 public WebServiceFeatureList(WebServiceFeatureList features) { 128 if (features != null) { 129 wsfeatures.putAll(features.wsfeatures); 130 parent = features.parent; 131 isValidating = features.isValidating; 132 } 133 } 134 135 /** 136 * Creates a list by reading featuers from the annotation on a class. 137 */ 138 public WebServiceFeatureList(@NotNull Class<?> endpointClass) { 139 parseAnnotations(endpointClass); 140 } 141 142 /** 143 * Adds the corresponding features to the list for feature annotations(i.e 144 * which have {@link WebServiceFeatureAnnotation} meta annotation) 145 * 146 * @param annIt collection of annotations(that can have non-feature annotations) 147 */ 148 public void parseAnnotations(Iterable<Annotation> annIt) { 149 for(Annotation ann : annIt) { 150 WebServiceFeature feature = getFeature(ann); 151 if (feature != null) { 152 add(feature); 153 } 154 } 155 } 156 157 /** 158 * Returns a corresponding feature for a feature annotation(i.e which has 159 * {@link WebServiceFeatureAnnotation} meta annotation) 160 * 161 * @return corresponding feature for the annotation 162 * null, if the annotation is nota feature annotation 163 */ 164 public static WebServiceFeature getFeature(Annotation a) { 165 WebServiceFeature ftr = null; 166 if (!(a.annotationType().isAnnotationPresent(WebServiceFeatureAnnotation.class))) { 167 ftr = null; 168 } else if (a instanceof Addressing) { 169 Addressing addAnn = (Addressing) a; 170 try { 171 ftr = new AddressingFeature(addAnn.enabled(), addAnn.required(),addAnn.responses()); 172 } catch(NoSuchMethodError e) { 173 //throw error. We can't default to Responses.ALL as we dont know if the user has not used 2.2 annotation with responses. 174 throw new RuntimeModelerException(ModelerMessages.RUNTIME_MODELER_ADDRESSING_RESPONSES_NOSUCHMETHOD(toJar(Which.which(Addressing.class)))); 175 } 176 } else if (a instanceof MTOM) { 177 MTOM mtomAnn = (MTOM) a; 178 ftr = new MTOMFeature(mtomAnn.enabled(), mtomAnn.threshold()); 179 } else if (a instanceof RespectBinding) { 180 RespectBinding rbAnn = (RespectBinding) a; 181 ftr = new RespectBindingFeature(rbAnn.enabled()); 182 } else { 183 ftr = getWebServiceFeatureBean(a); 184 } 185 return ftr; 186 } 187 188 /** 189 * 190 * @param endpointClass web service impl class 191 */ 192 public void parseAnnotations(Class<?> endpointClass) { 193 for (Annotation a : endpointClass.getAnnotations()) { 194 WebServiceFeature ftr = getFeature(a); 195 if (ftr != null) { 196 if (ftr instanceof MTOMFeature) { 197 // check conflict with @BindingType 198 BindingID bindingID = BindingID.parse(endpointClass); 199 MTOMFeature bindingMtomSetting = bindingID.createBuiltinFeatureList().get(MTOMFeature.class); 200 if (bindingMtomSetting != null && bindingMtomSetting.isEnabled() ^ ftr.isEnabled()) { 201 throw new RuntimeModelerException( 202 ModelerMessages.RUNTIME_MODELER_MTOM_CONFLICT(bindingID, ftr.isEnabled())); 203 } 204 } 205 add(ftr); 206 } 207 } 208 } 209 210 /** 211 * Given the URL String inside jar, returns the URL to the jar itself. 212 */ 213 private static String toJar(String url) { 214 if(!url.startsWith("jar:")) 215 return url; 216 url = url.substring(4); // cut off jar: 217 return url.substring(0,url.lastIndexOf('!')); // cut off everything after '!' 218 } 219 220 private static WebServiceFeature getWebServiceFeatureBean(Annotation a) { 221 WebServiceFeatureAnnotation wsfa = a.annotationType().getAnnotation(WebServiceFeatureAnnotation.class); 222 Class<? extends WebServiceFeature> beanClass = wsfa.bean(); 223 WebServiceFeature bean; 224 225 Constructor ftrCtr = null; 226 String[] paramNames = null; 227 for (Constructor con : beanClass.getConstructors()) { 228 FeatureConstructor ftrCtrAnn = (FeatureConstructor) con.getAnnotation(FeatureConstructor.class); 229 if (ftrCtrAnn != null) { 230 if (ftrCtr == null) { 231 ftrCtr = con; 232 paramNames = ftrCtrAnn.value(); 233 } else { 234 throw new WebServiceException( 235 ModelerMessages.RUNTIME_MODELER_WSFEATURE_MORETHANONE_FTRCONSTRUCTOR(a, beanClass)); 236 } 237 } 238 } 239 if (ftrCtr == null) { 240 bean = getWebServiceFeatureBeanViaBuilder(a, beanClass); 241 if (bean != null) { 242 return bean; 243 } else { 244 throw new WebServiceException( 245 ModelerMessages.RUNTIME_MODELER_WSFEATURE_NO_FTRCONSTRUCTOR(a, beanClass)); 246 } 247 } 248 if (ftrCtr.getParameterTypes().length != paramNames.length) { 249 throw new WebServiceException( 250 ModelerMessages.RUNTIME_MODELER_WSFEATURE_ILLEGAL_FTRCONSTRUCTOR(a, beanClass)); 251 } 252 253 try { 254 Object[] params = new Object[paramNames.length]; 255 for (int i = 0; i < paramNames.length; i++) { 256 Method m = a.annotationType().getDeclaredMethod(paramNames[i]); 257 params[i] = m.invoke(a); 258 } 259 bean = (WebServiceFeature) ftrCtr.newInstance(params); 260 } catch (Exception e) { 261 throw new WebServiceException(e); 262 } 263 return bean; 264 } 265 266 private static WebServiceFeature getWebServiceFeatureBeanViaBuilder( 267 final Annotation annotation, 268 final Class<? extends WebServiceFeature> beanClass) 269 { 270 try { 271 final Method featureBuilderMethod = beanClass.getDeclaredMethod("builder"); 272 final Object builder = featureBuilderMethod.invoke(beanClass); 273 final Method buildMethod = builder.getClass().getDeclaredMethod("build"); 274 275 for (Method builderMethod : builder.getClass().getDeclaredMethods()) { 276 if (!builderMethod.equals(buildMethod) && !builderMethod.isSynthetic()) { 277 final String methodName = builderMethod.getName(); 278 final Method annotationMethod = annotation.annotationType().getDeclaredMethod(methodName); 279 final Object annotationFieldValue = annotationMethod.invoke(annotation); 280 final Object[] arg = { annotationFieldValue }; 281 if (skipDuringOrgJvnetWsToComOracleWebservicesPackageMove(builderMethod, annotationFieldValue)) { 282 continue; 283 } 284 builderMethod.invoke(builder, arg); 285 } 286 } 287 288 final Object result = buildMethod.invoke(builder); 289 if (result instanceof WebServiceFeature) { 290 return (WebServiceFeature) result; 291 } else { 292 throw new WebServiceException("Not a WebServiceFeature: " + result); 293 } 294 } catch (final NoSuchMethodException e) { 295 LOGGER.log(Level.INFO, "Unable to find builder method on webservice feature: " + beanClass.getName(), e); 296 return null; 297 } catch (final IllegalAccessException e) { 298 throw new WebServiceException(e); 299 } catch (final InvocationTargetException e) { 300 throw new WebServiceException(e); 301 } 302 } 303 304 // TODO this will be removed after package move is complete. 305 private static boolean skipDuringOrgJvnetWsToComOracleWebservicesPackageMove( 306 final Method builderMethod, 307 final Object annotationFieldValue) 308 { 309 final Class<?> annotationFieldValueClass = annotationFieldValue.getClass(); 310 if (! annotationFieldValueClass.isEnum()) { 311 return false; 312 } 313 final Class<?>[] builderMethodParameterTypes = builderMethod.getParameterTypes(); 314 if (builderMethodParameterTypes.length != 1) { 315 throw new WebServiceException("expected only 1 parameter"); 316 } 317 final String builderParameterTypeName = builderMethodParameterTypes[0].getName(); 318 if (! builderParameterTypeName.startsWith("com.oracle.webservices.internal.test.features_annotations_enums.apinew") && 319 ! builderParameterTypeName.startsWith("com.oracle.webservices.internal.api")) { 320 return false; 321 } 322 return false; 323 } 324 325 public Iterator<WebServiceFeature> iterator() { 326 if (parent != null) 327 return new MergedFeatures(parent.getFeatures()); 328 return wsfeatures.values().iterator(); 329 } 330 331 public @NotNull 332 WebServiceFeature[] toArray() { 333 if (parent != null) 334 return new MergedFeatures(parent.getFeatures()).toArray(); 335 return wsfeatures.values().toArray(new WebServiceFeature[] {}); 336 } 337 338 public boolean isEnabled(@NotNull Class<? extends WebServiceFeature> feature) { 339 WebServiceFeature ftr = get(feature); 340 return ftr != null && ftr.isEnabled(); 341 } 342 343 public boolean contains(@NotNull Class<? extends WebServiceFeature> feature) { 344 WebServiceFeature ftr = get(feature); 345 return ftr != null; 346 } 347 348 public @Nullable 349 <F extends WebServiceFeature> F get(@NotNull Class<F> featureType) { 350 WebServiceFeature f = featureType.cast(wsfeatures.get(featureType)); 351 if (f == null && parent != null) { 352 return parent.getFeatures().get(featureType); 353 } 354 return (F) f; 355 } 356 357 /** 358 * Adds a feature to the list if it's not already added. 359 */ 360 public void add(@NotNull WebServiceFeature f) { 361 if(addNoValidate(f) && isValidating) 362 validate(f); 363 } 364 365 private boolean addNoValidate(@NotNull WebServiceFeature f) { 366 if (!wsfeatures.containsKey(f.getClass())) { 367 wsfeatures.put(f.getClass(), f); 368 369 if (f instanceof ImpliesWebServiceFeature) 370 ((ImpliesWebServiceFeature) f).implyFeatures(this); 371 372 return true; 373 } 374 375 return false; 376 } 377 378 /** 379 * Adds features to the list if it's not already added. 380 */ 381 public void addAll(@NotNull Iterable<WebServiceFeature> list) { 382 for (WebServiceFeature f : list) 383 add(f); 384 } 385 386 /** 387 * Sets MTOM feature, overriding any existing feature. This is necessary for compatibility 388 * with the existing {@link SOAPBinding.setMTOMEnabled}. 389 * @param b if MTOM will be enabled 390 */ 391 void setMTOMEnabled(boolean b) { 392 wsfeatures.put(MTOMFeature.class, new MTOMFeature(b)); 393 } 394 395 public boolean equals(Object other) { 396 if (!(other instanceof WebServiceFeatureList)) 397 return false; 398 399 WebServiceFeatureList w = (WebServiceFeatureList) other; 400 return wsfeatures.equals(w.wsfeatures) && (parent == w.parent); 401 } 402 403 public String toString() { 404 return wsfeatures.toString(); 405 } 406 407 /** 408 * Merges the extra features that are not already set on binding. 409 * i.e, if a feature is set already on binding through some other API 410 * the corresponding wsdlFeature is not set. 411 * 412 * @param features Web Service features that need to be merged with already configured features. 413 * @param reportConflicts If true, checks if the feature setting in WSDL (wsdl extension or 414 * policy configuration) conflicts with feature setting in Deployed Service and 415 * logs warning if there are any conflicts. 416 */ 417 public void mergeFeatures(@NotNull Iterable<WebServiceFeature> features, boolean reportConflicts) { 418 for (WebServiceFeature wsdlFtr : features) { 419 if (get(wsdlFtr.getClass()) == null) { 420 add(wsdlFtr); 421 } else if (reportConflicts) { 422 if (isEnabled(wsdlFtr.getClass()) != wsdlFtr.isEnabled()) { 423 LOGGER.warning(ModelerMessages.RUNTIME_MODELER_FEATURE_CONFLICT( 424 get(wsdlFtr.getClass()), wsdlFtr)); 425 } 426 } 427 } 428 } 429 430 public void mergeFeatures(WebServiceFeature[] features, boolean reportConflicts) { 431 for (WebServiceFeature wsdlFtr : features) { 432 if (get(wsdlFtr.getClass()) == null) { 433 add(wsdlFtr); 434 } else if (reportConflicts) { 435 if (isEnabled(wsdlFtr.getClass()) != wsdlFtr.isEnabled()) { 436 LOGGER.warning(ModelerMessages.RUNTIME_MODELER_FEATURE_CONFLICT( 437 get(wsdlFtr.getClass()), wsdlFtr)); 438 } 439 } 440 } 441 } 442 443 /** 444 * Extracts features from {@link WSDLPort#getFeatures()}. Extra features 445 * that are not already set on binding. i.e, if a feature is set already on 446 * binding through some other API the corresponding wsdlFeature is not set. 447 * 448 * @param wsdlPort 449 * WSDLPort model 450 * @param honorWsdlRequired 451 * If this is true add WSDL Feature only if wsd:Required=true In 452 * SEI case, it should be false In Provider case, it should be 453 * true 454 * @param reportConflicts 455 * If true, checks if the feature setting in WSDL (wsdl extension 456 * or policy configuration) conflicts with feature setting in 457 * Deployed Service and logs warning if there are any conflicts. 458 */ 459 public void mergeFeatures(@NotNull WSDLPort wsdlPort, 460 boolean honorWsdlRequired, boolean reportConflicts) { 461 if (honorWsdlRequired && !isEnabled(RespectBindingFeature.class)) 462 return; 463 if (!honorWsdlRequired) { 464 addAll(wsdlPort.getFeatures()); 465 return; 466 } 467 // Add only if isRequired returns true, when honorWsdlRequired is true 468 for (WebServiceFeature wsdlFtr : wsdlPort.getFeatures()) { 469 if (get(wsdlFtr.getClass()) == null) { 470 try { 471 // if it is a WSDL Extension , it will have required 472 // attribute 473 Method m = (wsdlFtr.getClass().getMethod("isRequired")); 474 try { 475 boolean required = (Boolean) m.invoke(wsdlFtr); 476 if (required) 477 add(wsdlFtr); 478 } catch (IllegalAccessException e) { 479 throw new WebServiceException(e); 480 } catch (InvocationTargetException e) { 481 throw new WebServiceException(e); 482 } 483 } catch (NoSuchMethodException e) { 484 // this wsdlFtr is not an WSDL extension, just add it 485 add(wsdlFtr); 486 } 487 } else if (reportConflicts) { 488 if (isEnabled(wsdlFtr.getClass()) != wsdlFtr.isEnabled()) { 489 LOGGER.warning(ModelerMessages.RUNTIME_MODELER_FEATURE_CONFLICT( 490 get(wsdlFtr.getClass()), wsdlFtr)); 491 } 492 493 } 494 } 495 } 496 497 /** 498 * Set the parent features. Basically the parent feature list will be 499 * overriden by this feature list. 500 */ 501 public void setParentFeaturedObject(@NotNull WSDLFeaturedObject parent) { 502 this.parent = parent; 503 } 504 505 public static @Nullable <F extends WebServiceFeature> F getFeature(@NotNull WebServiceFeature[] features, 506 @NotNull Class<F> featureType) { 507 for (WebServiceFeature f : features) { 508 if (f.getClass() == featureType) 509 return (F) f; 510 } 511 return null; 512 } 513 514 /** 515 * A Union of this WebServiceFeatureList and the parent. 516 */ 517 private final class MergedFeatures implements Iterator<WebServiceFeature> { 518 private final Stack<WebServiceFeature> features = new Stack<WebServiceFeature>(); 519 520 public MergedFeatures(@NotNull WSFeatureList parent) { 521 522 for (WebServiceFeature f : wsfeatures.values()) { 523 features.push(f); 524 } 525 526 for (WebServiceFeature f : parent) { 527 if (!wsfeatures.containsKey(f.getClass())) { 528 features.push(f); 529 } 530 } 531 } 532 533 public boolean hasNext() { 534 return !features.empty(); 535 } 536 537 public WebServiceFeature next() { 538 if (!features.empty()) { 539 return features.pop(); 540 } 541 throw new NoSuchElementException(); 542 } 543 544 public void remove() { 545 if (!features.empty()) { 546 features.pop(); 547 } 548 } 549 550 public WebServiceFeature[] toArray() { 551 return features.toArray(new WebServiceFeature[] {}); 552 } 553 } 554 555 private static final Logger LOGGER = Logger.getLogger(WebServiceFeatureList.class.getName()); 556 557 @Override 558 public Set<java.util.Map.Entry<Class<? extends WebServiceFeature>, WebServiceFeature>> entrySet() { 559 return wsfeatures.entrySet(); 560 } 561 562 @Override 563 public WebServiceFeature put(Class<? extends WebServiceFeature> key, WebServiceFeature value) { 564 return wsfeatures.put(key, value); 565 } 566 567 static public SOAPVersion getSoapVersion(WSFeatureList features) { 568 { 569 EnvelopeStyleFeature env = features.get(EnvelopeStyleFeature.class); 570 if (env != null) { 571 return SOAPVersion.from(env); 572 } 573 } 574 com.oracle.webservices.internal.api.EnvelopeStyleFeature env = features.get(com.oracle.webservices.internal.api.EnvelopeStyleFeature.class); 575 return env != null ? SOAPVersion.from(env) : null; 576 } 577 578 static public boolean isFeatureEnabled(Class<? extends WebServiceFeature> type, WebServiceFeature[] features) { 579 WebServiceFeature ftr = getFeature(features, type); 580 return ftr != null && ftr.isEnabled(); 581 } 582 583 static public WebServiceFeature[] toFeatureArray(WSBinding binding) { 584 //TODO scchen convert BindingID to WebServiceFeature[] 585 if(!binding.isFeatureEnabled(EnvelopeStyleFeature.class)) { 586 WebServiceFeature[] f = { binding.getSOAPVersion().toFeature() }; 587 binding.getFeatures().mergeFeatures(f, false); 588 } 589 return binding.getFeatures().toArray(); 590 } 591} 592