Install the NetBeans IDE. This example uses version 7.0.1.
You can use any Java IDE (Eclipse, JDeveloper, etc.) or just the JDK. But this example uses NetBeans IDE.
Download the binary for Apache Commons HttpClient.
Unzip HttpClient to a local folder.
Start NetBeans IDE.
From NetBeans, click File > New Project... > Java Application.
Click Next.
Change the Project Name.
Click Finish.
To add the HttpClient archives, right-click Libraries and select Add JAR/Folder...
Select the unzipped lib folder for HttpClient.
Highlight all of the JAR files.
Click Open.
Expand the org.apache.http.client node to see what you get.
This example uses HttpClient, so import that into the class file that contains the main method in you project.
package javahttpclienttest; import org.apache.http.client.*;
After you type import org, the possibilities should appear in the Auto Popup Completion Window.
To import anything org.apache.http...
We want to use a Business Operations Services SOAP endpoint. To find the endpoint, go to the Endpoint Reference for your Infinity application.
Click Business Operations Services.
Click the database.
For this example, click Code Tables.
Scroll to and click the link for TITLECODE.
Click GetTableEntryData.
The samples include requests and responses for SOAP 1.1 and SOAP 1.2.
Scroll to the SOAP 1.2 samples. Notice the two placeholders in the request: length for Content-Length in the HTTP header and guid inside the TableEntryID element in the SOAP envelope.
The length placeholder refers to length of the SOAP envelope. This is determined in our example with a string function. To find guid, we can look in the application. There are more robust ways to do this with the API. But grabbing the GUID from the application is sometimes useful and it will keep this example simpler.
Open the Infinity application and navigate to Administration > Application > Features. Under Features, click Code table search.
In Database table name, enter TITLECODE and click Search.
Select the result.
The summary section on the page that appears displays the GUID.
456ffd4c-0fbf-49db-a503-0726f86e2a39
So what we have at this point is a Java Application project maintained in NetBeans that imports some classes from Apache HttpClient. We also know how to find a sample SOAP request for the Infinity BizOps API. Finally, we know one way to find the GUID that the SOAP request requires using the Infinity application. Now lets go back to the NetBeans IDE and put these pieces together.
In the main method, add a line to instantiate an HttpClient. HttpClient is an interface, so we will use a default class that implements HttpClient.
HttpClient infapiclient = new DefaultHttpClient();
DefaultHttpClient is not contained in org.apache.http.client. So add this import for the class:
import org.apache.http.impl.client.DefaultHttpClient;
The first line (the request line) in either SOAP request sample (SOAP 1.1. or SOAP 1.2) in the endpoint reference tells us that HTTP request is a POST.
POST /bbAppFx/vpp/bizops/db%5BBBINFINITY%5D/codetables/TITLECODE/soap.asmx HTTP/1.1
Host: localhost
Content-Type: application/soap+xml; charset=utf-8
Content-Length: length
<?xml version="1.0" encoding="utf-8"?>
<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
<soap12:Body>
<GetTableEntryDataRequest xmlns="blackbaud_appfx_server_bizops">
<TableEntryID>guid</TableEntryID>
</GetTableEntryDataRequest>
</soap12:Body>
</soap12:Envelope>
Instantiate an HttpPost. What comes after POST in the endpoint reference sample is not fully formed. But let's leave it that way and see what happens.
HttpClient infapiclient = new DefaultHttpClient(); HttpPost infapiclientpost = new HttpPost ("/bbAppFx/vpp/bizops/db%5BBBINFINITY%5D/codetables/TITLECODE/soap.asmx");
Add an import to support HttpPost:
import org.apache.http.client.methods.HttpPost;
Now instantiate something to hold the response. We can also execute the request in the same line and assign the response to the request.
HttpResponse infresponse = infapiclient.execute(infapiclientpost);
And we need an import to support HttpResponse:
import org.apache.http.HttpResponse;
The IDE should alert you to potential exceptions. You have several options. But for now, just add a throws clause to the main method.
public static void main(String[] args) throws IOException {
Don't forget to import java.io.IOException.
import java.io.IOException;
Here is the current state of the file:
/* */ package javahttpclienttest; import java.io.IOException; import org.apache.http.HttpResponse; import org.apache.http.client.*; import org.apache.http.client.methods.HttpPost; import org.apache.http.impl.client.DefaultHttpClient; /** * * @author TomTr */ public class JavaHttpClientTest { /** * @param args the command line arguments */ public static void main(String[] args) throws IOException { // TODO code application logic here HttpClient infapiclient = new DefaultHttpClient(); HttpPost infapiclientpost = new HttpPost ("/bbAppFx/vpp/bizops/db%5BBBINFINITY%5D/codetables/TITLECODE/soap.asmx"); HttpResponse infresponse = infapiclient.execute(infapiclientpost); } }
Now you have an incomplete client and request along with a response. The post needs some more information. The HTTP header information is incomplete, and we need a SOAP envelope. But let's see what happens if we run this. Click Run > Run main project.
Well, it didn't like that. Particularly:
Exception in thread "main" java.lang.IllegalStateException: Target host must not be null, or set in parameters.
So let's change the URI in the HttpPost instantiation to include http://localhost/.
// Client HttpClient infapiclient = new DefaultHttpClient(); //Request HttpPost infapiclientpost = new HttpPost ("http://localhost/bbAppFx/vpp/bizops/db%5BBBINFINITY%5D/codetables/TITLECODE/soap.asmx"); //Response HttpResponse infresponse = infapiclient.execute(infapiclientpost);
Run it again. That should work better.
run: BUILD SUCCESSFUL (total time: 0 seconds)
Not much going on yet though. Let's look at the header. Usually one would listen to the traffic to see this. But we are going to add some temporary code. If you want to get an item from a header, you can use a method called getFirstHeader. Add this line after the request instantiation and run the main project.
System.out.println(infapiclientpost.getFirstHeader("Host").toString());
You should get an exception. That is because we haven't specified any header information for the HTTP request, just the request line. Add this line before the println:
infapiclientpost.addHeader("Host", "localhost");
Run it again.
run: Host: localhost BUILD SUCCESSFUL (total time: 1 second)
The console printed our Host, localhost. At this point, two other items are in the header: Content-Type and Content-Length for SOAP 1.2 and another, SOAPAction for SOAP 1.1. But let's not get ahead of ourselves. We don't have any content yet. Let's add some.
The content is a SOAP request. We have a sample for that SOAP request. The sample has two placeholders and we know one of them: guid.
456ffd4c-0fbf-49db-a503-0726f86e2a39
Let's create the text for the SOAP envelope by hand. It's the bottom part of the request sample. Use the SOAP 1.2 sample and replace guid with 456ffd4c-0fbf-49db-a503-0726f86e2a39.
<?xml version="1.0" encoding="utf-8"?>
<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
<soap12:Body>
<GetTableEntryDataRequest xmlns="blackbaud_appfx_server_bizops">
<TableEntryID>456ffd4c-0fbf-49db-a503-0726f86e2a39</TableEntryID>
</GetTableEntryDataRequest>
</soap12:Body>
</soap12:Envelope>
Put that in an XML file and add it to the project. Right-click the source package and click New > XML Document... You may have to navigate to Other.
Call it SOAPXMLGetTableEntryDataTitleCode.
Click Next and click Finish.
Clear the contents of the file, paste the SOAP XML into the file, and save it.
We will add the XML that contains the SOAP envelope to the HTTP request through an entity. First, let's instantiate the entity.
Add the following before the request instantiation.
//Entity HttpEntity infapiclientpostentity = new StringEntity("");
For now let's leave the string blank. You will also have to add imports for HttpEntity and StringEntity.
import org.apache.http.HttpEntity; import org.apache.http.entity.StringEntity;
In the Request section, add a line to set the entity for the HttpPost. Use the setEntity method.
infapiclientpost.setEntity(infapiclientpostentity);
And back in the Entity section, adjust as follows:
//Entity String postentitystring = new Scanner (new File("C:\\Users\\TomTr\\Documents\\NetBeansProjects\\JavaHttpClientTest\\src\\javahttpclienttest\\SOAPXMLGetTableEntryDataTitleCode.xml")).useDelimiter("\\A").next(); HttpEntity infapiclientpostentity = new StringEntity(postentitystring);
You will have to get the path for the file from the properties of the XML file in the project.
Reading the entire text of a file into a string is not something Java does naturally. You can create your own class and methods or use a third-party library as we do with HttpClient. The technique used here comes from this blog post.
We also want to see the response, so add the following to the end of the response section:
System.out.println(infresponse.toString());
To recap, your code should look like this:
/* */ package javahttpclienttest; import java.io.File; import java.io.IOException; import java.util.Scanner; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.*; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.DefaultHttpClient; /** * * @author TomTr */ public class JavaHttpClientTest { /** * @param args the command line arguments */ public static void main(String[] args) throws IOException { // Client HttpClient infapiclient = new DefaultHttpClient(); //Entity String postentitystring = new Scanner (new File("C:\\Users\\TomTr\\Documents\\NetBeansProjects\\JavaHttpClientTest\\src\\javahttpclienttest\\SOAPXMLGetTableEntryDataTitleCode.xml")).useDelimiter("\\A").next(); HttpEntity infapiclientpostentity = new StringEntity(postentitystring); //Request HttpPost infapiclientpost = new HttpPost ("http://localhost/bbAppFx/vpp/bizops/db%5BBBINFINITY%5D/codetables/TITLECODE/soap.asmx"); infapiclientpost.addHeader("Host", "localhost"); System.out.println(infapiclientpost.getFirstHeader("Host").toString()); infapiclientpost.setEntity(infapiclientpostentity); //Response HttpResponse infresponse = infapiclient.execute(infapiclientpost); System.out.println(infresponse.toString()); } }
When you build it, you should get.
run: Host: localhost HTTP/1.1 401 Unauthorized [Cache-Control: private, Content-Type: text/html; charset=utf-8, Server: Microsoft-IIS/7.5, WWW-Authenticate: Negotiate, WWW-Authenticate: NTLM, Date: Wed, 04 Jan 2012 19:40:28 GMT, Content-Length: 6591] BUILD SUCCESSFUL (total time: 0 seconds)
We still aren't out of the woods yet. We need to authenticate.