View Javadoc
1   /*
2    * Copyright (c) 1997, 2012, 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.api;
27  
28  import com.sun.istack.internal.Nullable;
29  
30  import javax.xml.ws.WebServiceException;
31  import java.io.IOException;
32  import java.net.MalformedURLException;
33  import java.net.Proxy;
34  import java.net.ProxySelector;
35  import java.net.URI;
36  import java.net.URISyntaxException;
37  import java.net.URL;
38  import java.net.URLConnection;
39  import java.net.URLStreamHandler;
40  import java.util.Iterator;
41  
42  /**
43   * Represents the endpoint address URI.
44   *
45   * <p>
46   * Conceptually this can be really thought of as an {@link URI},
47   * but it hides some of the details that improve the performance.
48   *
49   * <p>
50   * Being an {@link URI} allows this class to represent custom made-up URIs
51   * (like "jms" for example.) Whenever possible, this object
52   * also creates an {@link URL} (this is only possible when the address
53   * has a registered {@link URLStreamHandler}), so that if the clients
54   * of this code wants to use it, it can do so.
55   *
56   *
57   * <h3>How it improves the performance</h3>
58   * <ol>
59   *  <li>
60   *  Endpoint address is often eventually turned into an {@link URLConnection},
61   *  and given that generally this value is read more often than being set,
62   *  it makes sense to eagerly turn it into an {@link URL},
63   *  thereby avoiding a repeated conversion.
64   *
65   *  <li>
66   *  JDK spends a lot of time choosing a list of {@link Proxy}
67   *  to connect to an {@link URL}. Since the default proxy selector
68   *  implementation always return the same proxy for the same URL,
69   *  we can determine the proxy by ourselves to let JDK skip its
70   *  proxy-discovery step.
71   *
72   *  (That said, user-defined proxy selector can do a lot of interesting things
73   *  --- like doing a round-robin, or pick one from a proxy farm randomly,
74   *  and so it's dangerous to stick to one proxy. For this case,
75   *  we still let JDK decide the proxy. This shouldn't be that much of an
76   *  disappointment, since most people only mess with system properties,
77   *  and never with {@link ProxySelector}. Also, avoiding optimization
78   *  with non-standard proxy selector allows people to effectively disable
79   *  this optimization, which may come in handy for a trouble-shooting.)
80   * </ol>
81   *
82   * @author Kohsuke Kawaguchi
83   */
84  public final class EndpointAddress {
85      @Nullable
86      private URL url;
87      private final URI uri;
88      private final String stringForm;
89      private volatile boolean dontUseProxyMethod;
90      /**
91       * Pre-selected proxy.
92       *
93       * If {@link #url} is null, this field is null.
94       * Otherwise, this field could still be null if the proxy couldn't be chosen
95       * upfront.
96       */
97      private Proxy proxy;
98  
99      public EndpointAddress(URI uri) {
100         this.uri = uri;
101         this.stringForm = uri.toString();
102         try {
103             initURL();
104             proxy = chooseProxy();
105         } catch (MalformedURLException e) {
106             // ignore
107         }
108     }
109 
110     /**
111      *
112      * @see #create(String)
113      */
114     public EndpointAddress(String url) throws URISyntaxException {
115         this.uri = new URI(url);
116         this.stringForm = url;
117         try {
118             initURL();
119             proxy = chooseProxy();
120         } catch (MalformedURLException e) {
121             // ignore
122         }
123     }
124 
125 
126     private void initURL() throws MalformedURLException {
127         String scheme = uri.getScheme();
128         //URI.toURL() only works when scheme is not null.
129         if (scheme == null) {
130             this.url = new URL(uri.toString());
131             return;
132         }
133         scheme =scheme.toLowerCase();
134         if ("http".equals(scheme) || "https".equals(scheme)) {
135             url = new URL(uri.toASCIIString());
136         } else {
137             this.url = uri.toURL();
138         }
139     }
140 
141     /**
142      * Creates a new {@link EndpointAddress} with a reasonably
143      * generic error handling.
144      */
145     public static EndpointAddress create(String url) {
146         try {
147             return new EndpointAddress(url);
148         } catch(URISyntaxException e) {
149             throw new WebServiceException("Illegal endpoint address: "+url,e);
150         }
151     }
152 
153     private Proxy chooseProxy() {
154         ProxySelector sel =
155             java.security.AccessController.doPrivileged(
156                 new java.security.PrivilegedAction<ProxySelector>() {
157                     @Override
158                     public ProxySelector run() {
159                         return ProxySelector.getDefault();
160                     }
161                 });
162 
163         if(sel==null)
164             return Proxy.NO_PROXY;
165 
166 
167         if(!sel.getClass().getName().equals("sun.net.spi.DefaultProxySelector"))
168             // user-defined proxy. may return a different proxy for each invocation
169             return null;
170 
171         Iterator<Proxy> it = sel.select(uri).iterator();
172         if(it.hasNext())
173             return it.next();
174 
175         return Proxy.NO_PROXY;
176     }
177 
178     /**
179      * Returns an URL of this endpoint adress.
180      *
181      * @return
182      *      null if this endpoint address doesn't have a registered {@link URLStreamHandler}.
183      */
184     public URL getURL() {
185         return url;
186     }
187 
188     /**
189      * Returns an URI of the endpoint address.
190      *
191      * @return
192      *      always non-null.
193      */
194     public URI getURI() {
195         return uri;
196     }
197 
198     /**
199      * Tries to open {@link URLConnection} for this endpoint.
200      *
201      * <p>
202      * This is possible only when an endpoint address has
203      * the corresponding {@link URLStreamHandler}.
204      *
205      * @throws IOException
206      *      if {@link URL#openConnection()} reports an error.
207      * @throws AssertionError
208      *      if this endpoint doesn't have an associated URL.
209      *      if the code is written correctly this shall never happen.
210      */
211     public URLConnection openConnection() throws IOException {
212         if (url == null) {
213             throw new WebServiceException("URI="+uri+" doesn't have the corresponding URL");
214         }
215         if(proxy!=null && !dontUseProxyMethod) {
216             try {
217                 return url.openConnection(proxy);
218             } catch(UnsupportedOperationException e) {
219                 // Some OSGi and app server environments donot
220                 // override URLStreamHandler.openConnection(URL, Proxy) as it
221                 // is introduced in Java SE 5 API. Fallback to the other method.
222                 dontUseProxyMethod = true;
223             }
224         }
225         return url.openConnection();
226     }
227 
228     @Override
229     public String toString() {
230         return stringForm;
231     }
232 }