How can I use an HTTP proxy for a JAX-WS request without setting a system-wide property?

36,370

Solution 1

If you are using JAX-WS you might be able to set the socket factory used by the underlying HttpURLConnection. I see vague signs that this is possible for SSL (see HTTPS SSLSocketFactory) but I'm not certain if you can do that for regular HTTP connections (or quite frankly how that even works: the JAXWSProperties class they reference appears to be a non-standard JDK class).

If you can set the socket factory then you can configure a custom socket factory that uses the specific proxy you want.

Solution 2

I recommend using a custom ProxySelector. I had the same problem and it works great and is super flexible. It's simple too.

Here's my CustomProxySelector:

import org.hibernate.validator.util.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.*;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

/**
 * So the way a ProxySelector works is that for all Connections made,
 * it delegates to a proxySelector(There is a default we're going to
 * override with this class) to know if it needs to use a proxy
 * for the connection.
 * <p>This class was specifically created with the intent to proxy connections
 * going to the allegiance soap service.</p>
 *
 * @author Nate
 */
class CustomProxySelector extends ProxySelector {

    private final ProxySelector def;

    private Proxy proxy;

    private static final Logger logger = Logger.getLogger(CustomProxySelector.class.getName());

    private List<Proxy> proxyList = new ArrayList<Proxy>();

    /*
     * We want to hang onto the default and delegate
     * everything to it unless it's one of the url's
     * we need proxied.
     */
    CustomProxySelector(String proxyHost, String proxyPort) {
        this.def = ProxySelector.getDefault();
        proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, (null == proxyPort) ? 80 : Integer.valueOf(proxyPort)));
        proxyList.add(proxy);
        ProxySelector.setDefault(this);
    }

    @Override
    public List<Proxy> select(URI uri) {
        logger.info("Trying to reach URL : " + uri);
        if (uri == null) {
            throw new IllegalArgumentException("URI can't be null.");
        }
        if (uri.getHost().contains("allegiancetech")) {
            logger.info("We're trying to reach allegiance so we're going to use the extProxy.");
            return proxyList;
        }
        return def.select(uri);
    }

    /*
    * Method called by the handlers when it failed to connect
    * to one of the proxies returned by select().
    */
    @Override
    public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
        logger.severe("Failed to connect to a proxy when connecting to " + uri.getHost());
        if (uri == null || sa == null || ioe == null) {
            throw new IllegalArgumentException("Arguments can't be null.");
        }
        def.connectFailed(uri, sa, ioe);
    }
}
Share:
36,370
jbindel
Author by

jbindel

Fascinated with computer programming since reading LET A=20 LET B=5 LET C=A+B PRINT C in a Weekly Reader.

Updated on July 09, 2022

Comments

  • jbindel
    jbindel almost 2 years

    I have an application that needs to make a SOAP client request to a system on the Internet, so it needs to go though our HTTP proxy.

    One can do this by setting system-wide values such as system properties:

    // Cowboy-style.  Blow away anything any other part of the application has set.
    System.getProperties().put("proxySet", "true");
    System.getProperties().put("https.proxyHost", HTTPS_PROXY_HOST);  
    System.getProperties().put("https.proxyPort", HTTPS_PROXY_PORT);
    

    Or by setting the default ProxySelector (also a system-wide setting):

    // More Cowboy-style!  Every thing Google has found says to do it this way!?!?!
    ProxySelector.setDefault(new MyProxySelector(HTTPS_PROXY_HOST, HTTPS_PROXY_PORT));
    

    Neither of these is a wise choice if there is the possibility of other subsystems wanting to access web servers via different HTTP proxies or without any proxy. Using the ProxySelector would let me configure which connections use the proxy, but I would have to figure that out for every single thing in the huge application.

    A reasonable API would have a method that took a java.net.Proxy object just like the java.net.Socket(java.net.Proxy proxy) constructor does. That way the necessary settings are local to the part of the system that needs to set them. Is there some way to do this with a JAX-WS?

    I do not want to set a system-wide proxy configuration.

  • jbindel
    jbindel almost 13 years
    The JAXWSProperties doesn't appear to exist in my JDK 1.6.
  • jbindel
    jbindel almost 13 years
    Your method does work, and we did implement it successfully, but I decided to go with supported APIs and a JVM-wide setting of the SSLSocketFactory using HttpsURLConnection.setDefaultSSLSocketFactory(). We shall combine any keys and certs into a single versions of a default keystore and truststore.
  • Adriaan Koster
    Adriaan Koster about 12 years
    Do you mind sharing some details about your implementation of Femi's suggestion?
  • jbindel
    jbindel about 12 years
    I think that's similar to the approach I had with MyProxySelector.
  • Stalin Gino
    Stalin Gino over 9 years
    That link isn't having anything, found this instead. metro.java.net/2.1.1/guide/HTTPS_SSLSocketFactory.html
  • Michael Paesold
    Michael Paesold about 8 years
    Does anyone have an idea how to do that for non SSL transports?
  • Anonymous404
    Anonymous404 about 6 years
    Updated link shared by Stalin Gino : javaee.github.io/metro/doc/user-guide/…
  • Emdee
    Emdee almost 6 years
    How does that solve the problem described by the thread owner about setting the proxy in a system-wide fashion? To me it appears to be just a wrapper around ProxySelector.setDefault which is in fact the system-wide setting.
  • Uncle Iroh
    Uncle Iroh almost 6 years
    @Emdee You're right it is a system-wide Proxy selector in that every request will go through it -- however you can see if you look that based on the URI you're going to you could choose a different proxy (or no proxy) or whatever you need. That's the beauty of it -- it's flexible and let's you use whatever you need based on wherever you're pulling the information from. Though it's true in this case that the default happens to be the same as the one selected. It doesn't have to be that way -- it's a framework for choosing whatever you want.