Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Invoking Web Services in a Way That Works

DZone's Guide to

Invoking Web Services in a Way That Works

· Java Zone
Free Resource

What every Java engineer should know about microservices: Reactive Microservices Architecture.  Brought to you in partnership with Lightbend.

In this post I will explain how to invoke Web Services from within Eclipse. Just to be clear, I'm not discussing a full blown networking application. For that you would probably use the Eclipse Communications Framework (ECF). It is a simple case: one service that needs to be invoked. On the face of it, it should be very simple. This is Java, which was built for handling XMLs and web oriented tasks. There's a very popular Web Services stack called Apache Axis2. It is fairly simple to use and there are plenty of tutorials for using it. So why another tutorial?

The Challenge of Connecting From Eclipse

Not so fast. There's a "minor" catch here. You see, from my experience with my Eclipse plug-in, this will fail in many cases. Why? There are a number of reasons, most of them are around firewalls and proxies. Most users in the corporate world are sitting behind both a firewall and a proxy. For the invocation to work, the firewall should enable the communication from the Java process and the proxy needs to be properly defined in Eclipse.

Many firewalls today block the Java processes. Probably because it is just so easy to write a malicious Java applet that will sit on the client machine and send sensitive information to a remote attacker. In some distributions, the Eclipse process identifies as Eclipse and in other cases as plain Java. The behavior varies and it is hard to predict how the corporate firewall will react. As for proxy configuration: in Eclipse 3.3 this had to be defined manually in the Eclipse preferences. From Eclipse 3.4 there's a new default "system proxy configuration (if available)" settings. This might work in Windows. I'm not sure about other operating systems.

The bottom line is this: you just cannot rely on communicating with the outside world from within Eclipse. There's one exception to this, which makes this article worth writing: The SWT Browser Control.

The Browser control, like other SWT widgets, is a thin wrapper on top of native operating system UI elements. When you try to access the web from the browser control, this is the same as using the operating system default web browser. The identification of the process is the same ID as the browser so the firewall will allow the outgoing communications. The proxy settings are the settings the user configured in the browser. If the default web browser works, the browser component will work. This greatly improves the odds of getting through.

Sending a Request

Sending a request with the Browser widget is the same as invoking web services from a regular web browser. If the request can be a simple GET request, just enter a URL and send the request. If the request must be a POST request (e.g. due to the length of the parameters), you will need to have an HTML page with a form for submitting the request. We will deal with the simple GET case, so the code will look a bit like this:

Browser browser = new Browser(parent, SWT.NONE); 
browser.setVisible(false);
browser.setUrl(url); 

Of course, we need the parent element. If this is part of a UI operation, this should be no problem. If it is not, you can get the default shell, but this is not enough. You also need to make sure you will be running in the UI thread by using something that will look a bit like this:

PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() {
	public void run() {
 		Shell activeShell = PlatformUI.getWorkbench()
			.getActiveWorkbenchWindow().getShell();
		Browser browser = new Browser(activeShell, 
			SWT.NONE);
		browser.setVisible(false);
		browser.setUrl(url);
	}
}); 

Notice that this is a synchronous operation since we are looking for a response, and this is what we will do next.

Receiving a Response

The response will be available when the browser is finished processing. Since this is a synchronous operation, we need to wait until the process is complete. Naturally, we should also expect a timeout. The best way of finding out when the browser is ready is to use a process listener. The listener class will look a bit like this:

private static class BrowserTracker implements ProgressListener {
	boolean	complete = false;

	public void changed(ProgressEvent event) {
	}

	public void completed(ProgressEvent event) {
		complete = true;
	}
} 

I'm not using an anonymous class, because I want to access the complete variable. Using the tracker will look like this:

	...
browser.setVisible(false);
BrowserTracker tracker = new BrowserTracker();
browser.addProgressListener(tracker);
browser.setUrl(url);
...

After setting the URL we need to wait for the response. The UI must be free to process events, otherwise, we will never get any notification. This will look like this:

...
Display display = activeShell.getDisplay();
while (!tracker.complete) {
	if (!display.readAndDispatch()) {
		display.sleep();
	}
}
result = browser.getText();
... 

We still need to access the resulting text, which exists inside our anonymous class. To do that, we will convert our anonymous class to an inner class. The end result will look like this: (click here to get a complete Java file)

public static String sendRequest(final String url) {
	final BrowserRunnable runnable = new BrowserRunnable(url);
	PlatformUI.getWorkbench().getDisplay().syncExec(runnable);
	return runnable.result;
}

private static final class BrowserRunnable implements Runnable {
	final String	url;
	String			result	= null;

	private BrowserRunnable(final String url) {
		this.url = url;
	}

	public void run() {
		final Shell activeShell = PlatformUI
			.getWorkbench()
			.getActiveWorkbenchWindow().getShell();
		final Browser browser = new Browser(activeShell, 
			SWT.NONE);
		browser.setVisible(false);
		final BrowserTracker tracker = 
			new BrowserTracker();
		browser.addProgressListener(tracker);
		browser.setUrl(url);
		final Display display = activeShell.getDisplay();
		while (!tracker.complete) {
			if (!display.readAndDispatch()) {
				display.sleep();
			}
		}
		result = browser.getText();
	}
}

Note that the above code does not handle timeout or reporting progress to the user. I leave this to the reader and it is possible that I will cover this at a later post (let me know if you're interested). There's also a limitation to the above approach: it will not work while the platform is loading. You will have to wait until it is up and running.

Processing the Result

There is one big drawback to this approach: the result is what you get from the browser. Assuming your server is sending back an XML, this may be a problem since each browser has its' own way of presenting XMLs. Internet Explorer will add fancy expand/collapse controls while Firefox and Safari take a cleaner approach. When using browser.getText() you get the HTML from the browser (same as using "view source" in your browser) which may include CSS, JavaScipt and other tidbits. Since you cannot tell which browser will serve you, you cannot rely on the structure of the result.

In my case, I was controlling the server, too, so my solution was simple: send the response as one big encoded string with a unique prefix and suffix. While processing the response, the client would look for the prefix and suffix, and ignore everything in the middle. This solution works well on IE, Firefox and Safari on Windows, Mac OS X and Linux Ubuntu. This is fairly simple task. I used a piping of streams to serialize and de-serialize objects as one encoded string. The streams I used: ObjectOutputStream, GZIPOutputStream and Base64.OutputStream (from iharder.net/base64)

Summing Up

In this article I presented a practical approach for invoking a web service from inside Eclipse. Unlike usual communications done from within Eclipse, this method is almost guaranteed to succeed if the machine is connected to the internet. Got a simpler way? Let me know.

From http://blog.zvikico.com/

Microservices for Java, explained. Revitalize your legacy systems (and your career) with Reactive Microservices Architecture, a free O'Reilly book. Brought to you in partnership with Lightbend.

Topics:

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}