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