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