I have seen many times the creation of a .NET client for SDL Tridion's Core Service, but not once a Java client. In .NET / Visual Studio is extremely easy to create the proxy classes based on the service's WSDL. So how hard can it be in Java?
The entire code in this example is available in my Google Code project.
Wsimport.exe is part of the JDK (mine is located in C:\Progrm Files\Java\jdk1.6.0_33\bin), and in its simplest command-line syntax, it can be called like this:
wsimport.exe http://t2011sp1hr1.playground/webservices/CoreService2011.svc?wsdl
This command will load the WSDL and it will generate the Java proxy source classes. For me, using it was a breeze. It generated all proxies. I imported them in Eclipse and created my very first test client. It all went fine and the client worked. Shocking! :-)
UserData currentUser = client.getCurrentUser().getValue();
There were a few quirks, however: all objects (trafficked with the service) were wrapped inside JAXBElement objects. For example, strings were inside JAXBElement<String>, DateTime inside JAXBElement<XMLGregorianCalendar>, etc, according to the JAXB type convertion. I definitely didn't like that!
I put the XML above in file custombindings.xml and generated the proxies using the following command line:
The code below worked out-of-the-box (the entire Java client code is available on my Google code project). It uses the BasicHttp binding to read the current user, read a Component and create a new Folder:
UserData currentUser = client.getCurrentUser();
ReadOptions readOptions = new ReadOptions();
FolderData folder = (FolderData) client.getDefaultData(ItemType.FOLDER, "tcm:1-1-2");
The solution is to provide your own java.net.Authenticator that would provide your username and password during the authentication challenge.
The following is the Authenticator I used:
import java.net.Authenticator;
public class BasicHttpAuthenticator extends Authenticator {
private String user;
public BasicHttpAuthenticator(String user, String password) {
@Override
The final client code using the Authenticator is below:
private static final QName Q_NAME = new QName("http://www.sdltridion.com/ContentManager/CoreService",
public static void main(String[] args) throws Exception {
URL url = new URL("http://t2011sp1hr1.playground/webservices/CoreService2011.svc?wsdl");
UserData currentUser = client.getCurrentUser();
ReadOptions readOptions = new ReadOptions();
FolderData folder = (FolderData) client.getDefaultData(ItemType.FOLDER, "tcm:1-1-2");
Notice the URL parameter passed to the CoreService2011 constructor. This allows you to specify to which Core Service server to connect to.
The entire code in this example is available in my Google Code project.
Which Tool?
Doing a bit of research on the multiple ways of creating Java proxy classes for web services left me quite confused as to which framework / utility to use. They are quite a few (AXIS, JAXB, Apache CXF, XFire, NetBeans plugin, IDEA plugin, Eclipse plugin, etc). They seemed to be cumbersome to use, too specific, limited, quirky, buggy, etc. What I wanted was a simple generic too that worked. Enter wsimport.exe...Generating the Proxies
Note: Check out the update post available here http://yatb.mitza.net/2012/12/java-core-service-trouble-with-datetime.htmlWsimport.exe is part of the JDK (mine is located in C:\Progrm Files\Java\jdk1.6.0_33\bin), and in its simplest command-line syntax, it can be called like this:
wsimport.exe http://t2011sp1hr1.playground/webservices/CoreService2011.svc?wsdl
This command will load the WSDL and it will generate the Java proxy source classes. For me, using it was a breeze. It generated all proxies. I imported them in Eclipse and created my very first test client. It all went fine and the client worked. Shocking! :-)
CoreService2011 service = new CoreService2011();
ICoreService client = service.getBasicHttp();
UserData currentUser = client.getCurrentUser().getValue();
System.out.println(String.format("'%s' %s",
currentUser.getTitle().getValue(), currentUser.getId().getValue()));
currentUser.getTitle().getValue(), currentUser.getId().getValue()));
It turned out I should use a custom JAXB binding to get rid of the wrapping (see generateElementProperty) and to specify my own mapping between DateTime and java.util.Date (see documentation)):
<jxb:bindings version="1.0" xmlns:jxb="http://java.sun.com/xml/ns/jaxb" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<jxb:globalBindings generateElementProperty="false">
<jxb:javaType name="java.util.Date" xmlType="xs:dateTime"/>
</jxb:globalBindings>
</jxb:bindings>
wsimport.exe -d c:\temp\gendir -s c:\temp\srcdir -b c:\temp\custombindings.xml -verbose http://t2011sp1hr1.playground/webservices/CoreService2011.svc?wsdl
CoreService2011 service = new CoreService2011();
ICoreService client =
service.getBasicHttp();
UserData currentUser = client.getCurrentUser();
System.out.println(String.format("'%s' %s", currentUser.getTitle(), currentUser.getId()));
ReadOptions readOptions = new ReadOptions();
ComponentData component =
(ComponentData) client.read("tcm:1-852", readOptions);
System.out.println(String.format("'%s' %s", component.getTitle(), component.getId()));
FolderData folder = (FolderData) client.getDefaultData(ItemType.FOLDER, "tcm:1-1-2");
folder = (FolderData)
client.save(folder, readOptions);
System.out.println(String.format("'%s' %s", folder.getTitle(), folder.getId()));
Basic Authentication
By default, the Core Service will use NTLM authentication. In cases where you are running the client from another server (than the TCM), or from a server in a different network domain, you should be using Basic HTTP Authentication. The only problem is that the JAXB client does not work with Basic Authentication.The solution is to provide your own java.net.Authenticator that would provide your username and password during the authentication challenge.
The following is the Authenticator I used:
package mitza.coreservice.client;
import java.net.Authenticator;
import java.net.PasswordAuthentication;
public class BasicHttpAuthenticator extends Authenticator {
private String user;
private String password;
public BasicHttpAuthenticator(String user, String password) {
this.user = user;
this.password = password;
}
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(user, password.toCharArray());
}
}
public class Test {
private static final QName Q_NAME = new QName("http://www.sdltridion.com/ContentManager/CoreService",
"CoreService2011");
public static void main(String[] args) throws Exception {
BasicHttpAuthenticator
basicHttpAuthenticator = new BasicHttpAuthenticator(args[0], args[1]);
Authenticator.setDefault(basicHttpAuthenticator);
URL url = new URL("http://t2011sp1hr1.playground/webservices/CoreService2011.svc?wsdl");
CoreService2011 service = new CoreService2011(url, Q_NAME);
ICoreService client =
service.getBasicHttp();
UserData currentUser = client.getCurrentUser();
System.out.println(String.format("'%s' %s", currentUser.getTitle(), currentUser.getId()));
ReadOptions readOptions = new ReadOptions();
ComponentData component =
(ComponentData) client.read("tcm:1-852", readOptions);
System.out.println(String.format("'%s' %s", component.getTitle(), component.getId()));
FolderData folder = (FolderData) client.getDefaultData(ItemType.FOLDER, "tcm:1-1-2");
folder = (FolderData)
client.save(folder, readOptions);
System.out.println(String.format("'%s' %s", folder.getTitle(), folder.getId()));
}
}
Comments
I tried to follow, and apparently our installation is using 2010 version of the core service. The URL is something like this: http://myserver.com/webservices/CoreService.svc?wsdl
I generated all the proxy code and changed the code according below, and everything compiled OK. I am getting "Authentication failure" in the "getCurrentUser()" call, I am not sure why. (I am pretty sure I am using the right user and password)
private static final QName Q_NAME = new QName("http://www.sdltridion.com/ContentManager/CoreService", "CoreService");
BasicHttpAuthenticator basicHttpAuthenticator = new BasicHttpAuthenticator(user, passwd);
Authenticator.setDefault(basicHttpAuthenticator);
URL url = new URL("http://myserver.com/webservices/CoreService.svc?wsdl");
CoreService service = new CoreService(url, Q_NAME);
ICoreService2010 client = service.getBasicHttp2010();
UserData currentUser = client.getCurrentUser();
Additionally, check that your Core Service webservice (in IIS) accepts Basic Authentication (in my case it does not, yet it still works).
Also, try commenting out line Authenticator.setDefault... That will let the request go in as the 'current' logged in user. You will have to have the current user added in Tridion. See what you get.