|
|
||
Marc Hadley's BlogWeb Services and XML ArchivesJAX-RS Public Review Draft and JavaOnePosted by mhadley on May 02, 2008 at 12:44 PM | Permalink | Comments (3)The JAX-RS public review draft is now available for, er, review - download it here. If you are attending JavaOne we'd love to see you at any or all of the following sessions:
I'll also be on Glen's panel "REST Versus SOA: Can We All Just Get Along?" which follows our BOF in the same room at 21:30 (Tuesday is shaping up to be a busy day). JAX-RS ImplementationsPosted by mhadley on April 01, 2008 at 01:20 PM | Permalink | Comments (2)The Restlet team just announced a new release. Amongst the new features is support for JAX-RS, see an example here. With the RI (Jersey), Apache CXF, and JBoss RESTeasy, that makes a total of four implementations currently underway. The feedback we're receiving as a result of these parallel implementations is proving very useful and I'm grateful for the all the good input we've been getting. The graph below shows the monthly totals for emails sent to the dev, users, issues and commits mailing lists for both JAX-RS and Jersey.
As you can see, traffic on the lists has picked up quite a bit over the last few months and a good proportion of the increase is due to the additional implementation work flushing out issues. We've also been working through our issues list with a view to publishing a JCP public review draft in the not too distant future. Authentication in JerseyPosted by mhadley on March 07, 2008 at 01:14 PM | Permalink | Comments (4)I'm working on an internal project building some RESTful services using a combination of Jersey, JPA, Glassfish and Derby. Actions on some resources require authentication and I need access to the name of the authenticated user in the resource method. This entry describes the steps to set this up - kudos to my colleague Hubert for working out much of the below. Obviously you're going to need all the ingredients listed above. I'm using NetBeans which came bundled with Glassfish and Derby so all I had to do was install the RESTful Web Services plug-in, YMMV. Next you need a database to store username, password and group information. Here's the table definitions I used: CREATE TABLE users (
id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
username VARCHAR(64) UNIQUE NOT NULL,
password VARCHAR(64) NOT NULL,
);
CREATE INDEX username ON users(username);
CREATE TABLE groups (
id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
username VARCHAR(64) NOT NULL REFERENCES users(username) ON DELETE CASCADE,
groupname VARCHAR(64)
);
Depending on your needs you could lose the integer fields, the important thing is to have a column in the group table with the same name as the column in the users table that holds the username. Those two columns are used to join the users table to the groups table. The Glassfish security realm is going to execute a query like this to retrieve the groups (which map to security roles) that a particular user is a member of: SELECT groupname FROM groups g, users u where g.username = u.username and u.username = ? Add some users and groups for testing purposes, note that the password should be an md5 hash, not plaintext - there are several web sites that offer online MD5 generators that you can use. I used groups named USERS and ADMINISTRATORS, you'll see where these come in later. Next you need to define a JDBC data source, connection pool and security realm in Glassfish. The first two are pretty straightforward and will likely be taken care of already if you have deployed a web application using the database. Setting up the security realm is also straightforward once you have a suitable database structure as described above. You do this via the admin console as shown below (there are a couple of additional fields on the form, you can leave those blank): ![]() Its now possible to require authentication for access to a JAX-RS resource via web.xml. Say I have a resource that only authenticated users can use, e.g.: @Path("dropbox")
public class DropBox {
@Context
SecurityContext security;
@POST
public Response drop(InputStream data) {
String username = security.getUserPrincipal().getName();
...
}
}
Note the use of dependency injection to get an instance of <security-constraint>
<display-name>DropBox</display-name>
<web-resource-collection>
<web-resource-name>DropBox</web-resource-name>
<description></description>
<url-pattern>/dropbox</url-pattern>
<http-method>GET</http-method>
<http-method>POST</http-method>
<http-method>HEAD</http-method>
<http-method>PUT</http-method>
<http-method>OPTIONS</http-method>
<http-method>TRACE</http-method>
<http-method>DELETE</http-method>
</web-resource-collection>
<auth-constraint>
<description>Have to be a USER</description>
<role-name>USERS</role-name>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>userauthn</realm-name>
</login-config>
<security-role>
<description/>
<role-name>USERS</role-name>
</security-role>
That about it, after you redeploy the application, the next time you try to access Integrating Jersey and AbderaPosted by mhadley on February 05, 2008 at 11:38 AM | Permalink | Comments (4)I'm working on an internal project that involves adding Atom Publishing Protocol support to a data store. Naturally, I'm using Jersey for the HTTP side of things and decided to give Apache Abdera a try for simplifying working with feeds and entries. With JAX-RS I can write a feed resource pretty easily: import java.net.URI;
import java.util.Date;
import javax.ws.rs.ConsumeMime;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.ProduceMime;
import javax.ws.rs.UriParam;
import javax.ws.rs.core.HttpContext;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import org.apache.abdera.model.Entry;
import org.apache.abdera.model.Feed;
@ProduceMime("application/atom+xml")
@ConsumeMime("application/atom+xml")
@Path("myfeed")
public class FeedResource {
@HttpContext
private UriInfo uriInfo;
@GET
public Feed getFeed() {
Feed f = AbderaSupport.getAbdera().getFactory().newFeed();
f.setTitle("...");
f.setId(...);
f.addAuthor(...);
f.setUpdated(...);
URI feedLink = uriInfo.getRequestUri();
f.addLink(feedLink.toString(),"self");
for (...) {
Entry e = f.addEntry();
URI entryLink = ...
f.addLink(entryLink.toString(),"alternate");
...
}
return f;
}
@POST
public Response addEntry(Entry e) {
Entry newEntry = AbderaSupport.getAbdera().newEntry();
URI entryLink = ...;
...
return Response.created(entryLink).entity(newEntry).build();
}
}
The To allow use of Abdera's import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.ws.rs.ProduceMime;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.MessageBodyReader;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;
import org.apache.abdera.Abdera;
import org.apache.abdera.model.Document;
import org.apache.abdera.model.Element;
import org.apache.abdera.model.Entry;
import org.apache.abdera.model.Feed;
@Provider
@ProduceMime("application/atom+xml")
@ConsumeMime("application/atom+xml")
public class AbderaSupport implements MessageBodyWriter<Object>,
MessageBodyReader<Object> {
private final static Abdera abdera = new Abdera();
public static Abdera getAbdera() {
return abdera;
}
public long getSize(Object arg0) {
return -1;
}
public boolean isWriteable(Class<?> type) {
return (Feed.class.isAssignableFrom(type) || Entry.class.isAssignableFrom(type));
}
public void writeTo(Object feedOrEntry, MediaType mediaType,
MultivaluedMap<String, Object> headers,
OutputStream outputStream) throws IOException {
if (feedOrEntry instanceof Feed) {
Feed feed = (Feed)feedOrEntry;
Document<Feed> doc = feed.getDocument();
doc.writeTo(outputStream);
} else {
Entry entry = (Entry)feedOrEntry;
Document<Entry> doc = entry.getDocument();
doc.writeTo(outputStream);
}
}
public boolean isReadable(Class<?> type) {
return (Feed.class.isAssignableFrom(type) || Entry.class.isAssignableFrom(type));
}
public Object readFrom(Class<Object> feedOrEntry, MediaType mediaType,
MultivaluedMap<String, String> headers,
InputStream inputStream) throws IOException {
Document<Element> doc = getAbdera().getParser().parse(inputStream);
Element el = doc.getRoot();
if (feedOrEntry.isAssignableFrom(el.getClass())) {
return el;
} else {
throw new IOException("Unexpected payload, expected "+feedOrEntry.getName()+
", received "+el.getClass().getName());
}
}
}
Notice I actually implemented Abdera is shaping up to be a nice API for working with Atom documents. Hopefully the above demonstrates how easy it is to integrate support for new formats into Jersey. WADLing in JerseyPosted by mhadley on December 21, 2007 at 01:43 PM | Permalink | Comments (0)For a long time you've been able to retrieve a WADL document that describes the set of resources that make up a Jersey application. However, the returned WADL was generated at compile time and would only include resources that were statically known. In recent weeks we've switched many parts of Jersey including the WADL subsystem to use a shared abstract resource model that is built at runtime. This has allowed us to add some additional functionality that I'll describe here. The main new feature is that you can now retrieve a WADL for an individual resource. The resource can be a static resource which is described in the application WADL or it can be a resource for which you've received a link in some returned representation. For an example, consider the Simple Console example in the Jersey distribution. This example has two resource: http://127.0.0.1:9998/resources/form http://127.0.0.1:9998/resources/form/colours If you retrieve the "global" WADL (available at <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <application xmlns="http://research.sun.com/wadl/2006/10"> <resources base="http://localhost:9998/resources/"> <resource path="/form"> <method name="GET"> <response> <representation mediaType="text/html" /> </response> </method> <method name="POST"> <request> <representation mediaType="application/x-www-form-urlencoded" /> </request> <response> <representation mediaType="text/html" /> </response> </method> <resource path="colours" /> </resource> </resources> </application> From this you can see that you can get the "form" resource and post a HTML form to it and both will return some HTML. Also notice that there's a resource element with path
@Path("/form")
@ProduceMime("text/html")
public class Form {
@Path("colours")
public Colours getColours() {
return coloursResource;
}
...
}
With the latest changes to Jersey you can now get information on such dynamic resources by GETing the resource with an GET /resources/form/colours Accept: application/vnd.sun.wadl+xml will return: <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <application xmlns="http://research.sun.com/wadl/2006/10"> <resources base="http://localhost:9998/resources/"> <resource path="form/colours"> <method name="GET"> <request> <param xmlns:xs="http://www.w3.org/2001/XMLSchema" type="xs:string" style="query" name="match" /> </request> <response> <representation mediaType="text/plain" /> </response> </method> <method name="GET"> <request> <param xmlns:xs="http://www.w3.org/2001/XMLSchema" type="xs:string" style="query" name="match" /> </request> <response> <representation mediaType="application/json" /> </response> </method> </resource> </resources> </application> which corresponds to the resource class Colours.java. The above works except in the case that the resource supports the GET method but does not declare the supported media types (equivalent to All of the above is currently implemented in the trunk and will be included in the forthcoming 0.5 snapshot release. Integrating Jersey and GuicePosted by mhadley on November 19, 2007 at 07:13 AM | Permalink | Comments (0)Further to my recent post about integrating Jersey and Spring, Christian Rivasseau shows how to integrate Jersey and Guice. As Paul notes in comments to that post, we plan to further simplify integration of template engines like Freemarker but its great to see the existing resource provider SPI working for another IoC framework.
Early Draft Review of JSR 311Posted by mhadley on November 05, 2007 at 08:12 AM | Permalink | Comments (0)The first early draft of JSR 311 is now available for review, download the spec and Javadoc here. As the name suggests this is an early draft and the specification is far from complete but it does provide an early look at the current API. You can also find a list of current issues here. Please send feedback on the specification, Javadocs or current issues to users [at] jsr311.dev.java.net. Integrating Jersey and SpringPosted by mhadley on September 14, 2007 at 09:18 AM | Permalink | Comments (10)To further test the new resource provider SPI I described earlier I thought I'd try building a new resource provider that defers to Spring for resource creation. Note that I've never used Spring before so if there are better ways to accomplish what I did please let me know. I also ran into a problem that limited the integration more than I'd prefer (details at the end of this post). The Jersey dev list would be a great place to post any suggestions on how to improve the integration. ImplementationThe first task was to write a public Object getInstance(ResourceProviderContext context) {
try {
initSpringContext(context);
initBeanName();
Object resource = springContext.getBean(beanName, resourceClass);
context.injectDependencies(resource);
return resource;
} catch (Exception ex) {
throw new ContainerException("Unable to create resource", ex);
}
}
The protected static synchronized void initSpringContext(
ResourceProviderContext context) {
if (springContext==null) {
DummyResource r = new DummyResource();
context.injectDependencies(r);
springContext = WebApplicationContextUtils.getRequiredWebApplicationContext(
r.servletConfig.getServletContext());
}
}
public static class DummyResource {
@Resource
public ServletConfig servletConfig;
}
Jersey is based on annotated classes whereas Spring is based on named beans. The Spring protected synchronized void initBeanName() {
if (beanName==null) {
String names[] = springContext.getBeanNamesForType(resourceClass);
if (names.length==0)
throw new RuntimeException("No configured bean for "+resourceClass.getName());
else if (names.length>1)
throw new RuntimeException("Multiple configured beans for "+resourceClass.getName());
beanName=names[0];
}
}
I couldn't see any straightforward way to determine which bean to use for a given resource class if there is more than one defined at the top level so the provider requires a single top-level bean for each resource class. The final task was to define a new annotation that instructs the Jersey runtime to use the Spring resource provider: @Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@ResourceFactory(SpringProvider.class)
public @interface SpringFactory {}
ExampleWith the above implemented you can now write a resource class whose instantiation is controlled by Spring: @UriTemplate("{id}")
@SpringFactory
public class SpringResource {
private String name;
public SpringResource() {
name="unset";
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@HttpMethod("GET")
@ProduceMime("text/plain")
public String getDescription() {
return "Name: "+getName();
}
}
With the following <beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<bean id="bean1" scope="prototype" class="com.sun.ws.rest.spring.resources.SpringResource">
<property name="name" value="Mr. Bean"/>
</bean>
</beans>
The complete code is available here. No libraries are included in the ZIP file so you'll have to patch up the references to the required Jersey and Spring libraries. Integration ShortcomingI couldn't see any way to add support for Jersey-defined resources as constructor parameters. In an earlier post I described the new support for non-empty constructors. When using the Spring resource provider, the constructor can only include resources defined within the Spring configuration, not values supplied by Jersey. Is there a Spring API I could use to programatically add support for Jersey-defined constructor parameters ? NetBeans Jersey ToolingPosted by mhadley on September 10, 2007 at 12:37 PM | Permalink | Comments (0)A couple of video's demonstrating NetBeans 6 support for creating Jersey-based Web services: This is an up-to-date version of the demo we showed in our JavaOne session this year. New Jersey FeaturesPosted by mhadley on September 07, 2007 at 09:05 AM | Permalink | Comments (0)I recently merged the experimental "resourcefactory" branch back into the main Jersey trunk. The new code includes the following features and changes:
Details on these changes below Lifecycle ChangesPrior to the merge the Jersey runtime would create a single instance of a resource class and use that for all requests. Resource classes had to be re-entrant and thread safe. Following the merge the default scope for a resource object is per-request, i.e. the Jersey runtime creates a new instance of a resource class for each request. This change brings Jersey back in line with the current JSR 311 specification which requires a default per-request life-cycle. The old behavior can be recovered by annotating a resource class with the Non-empty ConstructorsPrior to the merge a resource class was required to have a zero argument constructor that the Jersey runtime would use when creating the singleton instance. Following the merge this requirement has been relaxed such that constructors can now have arguments. This in conjunction with the change to a default per-request lifecycle allows a more natural use of constructors to perform instance initialization, e.g.: @UriTemplate("entries/{id}")
public class Entry {
EntryEntity entity;
public Entry(@UriParam("id") String id) {
entity = findEntryEntity(id);
...
}
@HttpMethod
@ProduceMime({"application/json", "application/xml"})
public EntryEntity getEntity() {
return entity;
}
}
The arguments allowed in a resource class constructor depends on the resource provider used to create an instance of the resource class (see the description of the new SPI below). For default per-request resource classes you can use any combination of parameters annotated with Resource Provider SPIThe new resource provider SPI allows plugging-in of custom resource class factories and is used internally to support the per-request and singleton life-cycles. If you're not planning to develop a new resource provider you can safely stop reading here unless of course you are curious as to how the above features are implemented. A resource provider is responsible for creating instances of resource classes for the Jersey runtime to use. A resource provider implements The Jersey runtime identifies the resource provider to use for a particular resource class by examining the annotations on that resource class. It examines each class-level annotation in turn looking for a @Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@ResourceFactory(SingletonProvider.class)
public @interface Singleton {}
Note the The Its hoped that this SPI will support integration of Jersey with a variety of frameworks. Let us know if anything is unclear or needs improvement. Comments on JSR 311Posted by mhadley on August 17, 2007 at 02:57 PM | PermalinkPatrick Mueller has some nice things to say about JSR 311 but also has some concerns that I'd like to address. The first and easiest to address is the license under which the RI (Jersey) is distributed. Rather than GPL as Patrick states, Jersey is actually available under the CDDL license. Apologies if that wasn't entirely clear from the Jersey home page, I've just fixed that. Patrick also raised a metaphorical eyebrow over some of the non-goals listed in the latest editors draft of the specification (PDF): Support for Java versions prior to J2SE 5.0: The API will make extensive use of annotations and will require J2SE 5.0 or later. Read: Sorry, J2ME. Sorry, folks stuck on Java 1.4. Yes, certainly it would have been good to be able to offer something to Java 1.4 users but we felt that an annotation-based approach offered the best solution going forward and decided to focus on that. On the Java ME side, the fact that 311 is primarily a server side API (see the final point) means it is less suitable for the ME environment anyway. Description, registration and discovery: The specification will neither define nor require any service description, registration or discovery capability. Read: We'll figure this out later; hopefully we won't have to change anything in this spec once we start thinking about this aspect of the problem. Several folks whose opinions I respect think that description is antithetical to REST and given that there's no accepted standard for describing RESTful Web services (beyond the resource representations themselves) its hard to see what we could have done here. Certainly I have my own favorite description language and Jersey will automatically produce a WADL description of a set of JSR 311 resource classes but it would be wrong to require support for something like WADL in JSR 311. Client APIs: The specification will not define client-side APIs. Other specifications are expected to provide such functionality. Read: We'll figure this out later; hopefully we won't have to change anything in this spec once we start thinking about this aspect of the problem. The goal of JSR 311 is to make it simple to write RESTful Web services in Java. Of course we're keeping an eye out for classes and interfaces that might be useful on the client side (those concerned with URI manipulation seem like reasonable candidates) but the primary focus is on exposing Java objects as RESTful services so I don't think there's too much danger of unexpected overlap with any new client-side HTTP API that might emerge in the future. Comments on JSR 311Posted by mhadley on August 17, 2007 at 02:57 PM | PermalinkPatrick Mueller has some nice things to say about JSR 311 but also has some concerns that I'd like to address. The first and easiest to address is the license under which the RI (Jersey) is distributed. Rather than GPL as Patrick states, Jersey is actually available under the CDDL license. Apologies if that wasn't entirely clear from the Jersey home page, I've just fixed that. Patrick also raised a metaphorical eyebrow over some of the non-goals listed in the latest editors draft of the specification (PDF): Support for Java versions prior to J2SE 5.0: The API will make extensive use of annotations and will require J2SE 5.0 or later. Read: Sorry, J2ME. Sorry, folks stuck on Java 1.4. Yes, certainly it would have been good to be able to offer something to Java 1.4 users but we felt that an annotation-based approach offered the best solution going forward and decided to focus on that. On the Java ME side, the fact that 311 is primarily a server side API (see the final point) means it is less suitable for the ME environment anyway. Description, registration and discovery: The specification will neither define nor require any service description, registration or discovery capability. Read: We'll figure this out later; hopefully we won't have to change anything in this spec once we start thinking about this aspect of the problem. Several folks whose opinions I respect think that description is antithetical to REST and given that there's no accepted standard for describing RESTful Web services (beyond the resource representations themselves) its hard to see what we could have done here. Certainly I have my own favorite description language and Jersey will automatically produce a WADL description of a set of JSR 311 resource classes but it would be wrong to require support for something like WADL in JSR 311. Client APIs: The specification will not define client-side APIs. Other specifications are expected to provide such functionality. Read: We'll figure this out later; hopefully we won't have to change anything in this spec once we start thinking about this aspect of the problem. The goal of JSR 311 is to make it simple to write RESTful Web services in Java. Of course we're keeping an eye out for classes and interfaces that might be useful on the client side (those concerned with URI manipulation seem like reasonable candidates) but the primary focus is on exposing Java objects as RESTful services so I don't think there's too much danger of unexpected overlap with any new client-side HTTP API that might emerge in the future. JerseyPosted by mhadley on June 20, 2007 at 12:47 PM | Permalink
It seems we've already had our first bug report, keep them coming. What Leonard SaidPosted by mhadley on June 06, 2007 at 02:39 PM | PermalinkI've been offline on holiday in the UK for the last couple of weeks and it seems I've been missing out on an interesting debate about whether a description language for RESTful services is useful or not. The debate so far is nicely summarized by Arnon Rotem-Gal-Oz in Debate: Does REST Need a Description Language?. Obviously as the author of the WADL specification I think there is some utility in such a language and normally I'd be tempted to write a longer post addressing some of the points raised but rather than do that I'll instead direct you to Leonard Richardson's It's Just A Hypermedia Format which covers most of the things I'd have said had they not already been expressed so well. One thing I would like to add since many people are concerned with the link to XML schema: the element attribute of the WADL representation element is optional; WADL can use XML schemas but it isn't built on them. In fact WADL doesn't presume XML will be used for entity bodies and it can easily describe an application built using other media types. Google REST DescribePosted by mhadley on April 26, 2007 at 10:06 AM | Permalink | Comments (2)Thomas Steiner just released the first working demo of Google REST Describe. This tool takes a novel approach to creating WADL descriptions of Web applications by analyzing sample requests and responses and applying heuristics to extract descriptive information. You can then manually add or modify metadata to improve the automatically generated description. Very cool. Check it out here. Sun Web Developer Pack R1 with WADL and RESTful WS goodiesPosted by mhadley on March 13, 2007 at 10:02 AM | Permalink | Comments (0)As Dave already blogged: "The Sun Web Developer Pack (SWDP) finally uncloaked today". In addition to the items Dave mentioned I'd also like to point out that the pack contains prebuilt binaries of some WADL tools and an early access API and runtime for development of RESTful Web services. I blogged earlier about JSR 311, I expect the API produced by JSR 311 will differ significantly to the RESTful WS API in SWDP R1 but the experience we gained building it and the runtime to support it has been very useful for testing some of our early assumptions and clarifying our thinking about the JSR. We hope you'll take it for a spin and let us know what you think. Google WADL ToolsPosted by mhadley on March 12, 2007 at 11:50 AM | Permalink | Comments (0)Thomas Steiner has announced plans for a couple of tools to simplify RESTful development based around WADL. Tentatively titled "Google REST Compile" and "Google REST Describe", the first will read a WADL description and output client code for a variety of programming languages whilst the second will aid in producing a WADL file from an existing textual description. I'm delighted to see others picking up on WADL and hope the project goes well. JSR 311: Java API for RESTful Web ServicesPosted by mhadley on February 14, 2007 at 01:10 PM | PermalinkI was planning to wait until after the ballot to blog this but it seems lots of folks have already noticed and blogged about it so I figure I might as well put my 2p in now. Reaction so far has been mixed, ranging from "cool... I hope they don't screw it up" through "please ask Sun to reconsider this proposal" so I thought I'd try to address a couple of the negative perceptions noted so far.
A couple of people also complained that the JSR wasn't very specific and didn't include much in the way of detail. JSRs are like that, they describe the problem not the solution. FWIW, here's the sort of thing I currently have in mind though, of course, the final APIs will probably be very different following expert group discussions.
@UriTemplate("widgets/{widgetid}")
@ConsumeMime("application/widgets+xml")
@ProduceMime("application/widgets+xml")
public class Widget {
@HttpMethod(GET)
public Representation getWidget(@UriParam("widgetid") String id) {
String replyStr = getWidgetAsXml(id);
return new StringRepresentation(replyStr,
"application/widgets+xml");
}
@HttpMethod(PUT)
public void updateWidget(@UriParam("widgetid") String id,
Representation<Source> update) {
updateWidgetFromXml(id, update);
}
@LastModified
public Date getChangeDate(@UriParam("widgetid") String id) {
return getLastChanged(id);
}
}
The above is a resource with a URI of URI TemplatingPosted by mhadley on October 05, 2006 at 08:40 AM | Permalink | Comments (2)One of the features added to WADL in the latest revision of the specification was the ability to embed parameters in resource path definitions. E.g., the following describes a set of resources identified by a parameterized URI: <resources base="http://example.com/">
<resource path="applications/widgets/{widget_id}">
...
</resource>
<resources>
The above extract describes a set of resources identified by the URIs conforming to the following template: http://example.com/applications/widgets/{widget_id}
Where Defining URIs in this way is useful in a number of scenarios and for the last few months I've been working with Joe Gregorio, Mark Nottingham, David Orchard, James Snell and DeWitt Clinton on a more general specification for parameterized URIs which we are calling URI Templates. The first draft of the specification has now been published as an Internet Draft. To provide feedback on the specification, join the W3C URI mailing list. URI Templates are deliberately simple; WADL offers a mechanism by which additional metadata can be supplied for a template variable, e.g.: <resources base="http://example.com/">
<resource path="applications/widgets/{widget_id}">
<param name="widget_id" style="plain" type="xsd:int">
<doc xml:lang="en">
<xhtml:p>Specifies the identifier of a particular widget.</xhtml:p>
</doc>
</param>
...
</resource>
<resources>
When the value of the The Announcing wadl.dev.java.netPosted by mhadley on September 29, 2006 at 12:31 PM | Permalink | Comments (3)I'm pleased to announce the formation of a new project on java.net for the development of WADL and associated tools: http://wadl.dev.java.net/. The goals of the project are spelled out here. I've primed the pump with an initial release of a tool that generates Java stubs for Web applications described using WADL. Also included is a sample application that demonstrates use of the tool with the Yahoo News Search Service. The code implements the ideas I blogged here but is still beta quality. I'm hoping public availability will help flush out the bugs and highlight areas that need more work. If you are interested in WADL and would like to help then please consider joining the project - there are many ways to contribute. WADL RevisionPosted by mhadley on August 03, 2006 at 07:43 AM | Permalink | Comments (2)In a previous entry I described some enhancements that were under consideration for an updated version of the WADL specification. The resulting updated WADL specification is now available along with the associated W3C XML Schema and RelaxNG Schema. In revising the specification I made a couple of breaking changes to simplify the language and improve clarity so this version of the specification uses a new XML namespace for the WADL elements. Here's what's new: Resource ReferencesAs previously described, Identification of LinksAs previously described, a DocumentationA <application xmlns="..." xmns:xhtml="...">
<doc xml:lang="en" title="Online widget catalogue">
<xhtml:p>An online widget catalogue for all your widgeting needs.</xhtml:p>
</doc>
<resources base="http://example.com/">
<doc xml:lang="en" title="Commercial widgets">
<xhtml:p>Widgets for the commercial sector.</xhtml:p>
</doc>
...
</resources>
</application>
Multiple sibling Resource ReworkA <resources base="http://example.com/">
<resource path="applications/widgets/{widgetId}">
...
</resource>
</resources>
The above extract describes a set of resources identified by the URIs conforming to the following template: http://example.com/applications/widgets/{widgetId}
Where Parameter ReworkThe The A new Future WorkWith these changes I'm now pretty happy with the base language but there remain a couple of things that still need more work:
I believe that both of the above can be addressed with non-breaking extensions to the language. WADL EnhancementsPosted by mhadley on June 30, 2006 at 01:07 PM | Permalink | Comments (5)In this entry I describe some of the language enhancements that will be in the next version of the WADL specification. These enhancements are the result of feedback I've received from other folks using the language and experience gained implementing a Java processor for WADL.
Resource ReferencesCurrently you can define a method or representation in one place and then include that definition by reference elsewhere. This is useful if the same representation is used in multiple method definitions or if the same method applies to more than one resource. Extending this capability to the <application>
<resources base="http://example.com/">
<resource href="stock">
<resource href="#widgets"/>
</resource>
<resource href="order">
<resource href="#widgets"/>
</resource>
</resources>
<resource id="widgets" path="widgets">
...
</resource>
</application>
Identification of LinksWhilst it would be possible at runtime to compare a URI embedded in a representation with the URIs of all of the resources in a WADL description to identify which resource it identifies, it would be preferable to be able to specify the resource targeted by a link up front where that information is known.
This is achieved in WADL by adding a new element to the language: E.g., if an application consists of the resources:
and a GET on the first resource gets me a <eg:WidgetList xmlns:eg="..."> <eg:Widget uri="http://example.com/widgets/xyzzy1"/> <eg:Widget uri="http://example.com/widgets/abccb1"/> ... </eg:WidgetList> such that <application xmlns:...>
<resources base="http://example.com/">
<resource uri="widgets">
<method href="#widgetList"/>
<resource id="widgetResource" uri="{widgetid}">
...
</resource>
</resource>
</resources>
<method name="GET" id="widgetList">
<response>
<representation mediaType="application/xml" element="eg:WidgetList">
<representation_variable path="/Widgets/Widget/@uri" repeating="true">
<link href="#widgetResource" rel="child" rev="index"/>
</representation_variable>
</representation>
</response>
</method>
</application>
The values of the DocumentationThe content model of WADL-defined elements is open for extension and I originally thought that documentation could be added by including html or xhtml elements within a WADL description. Thinking on this some more I've come to the conclusion that it would be better to define a standard <application>
<documentation><p>An online widget catalogue.</p></documentation>
<resources base="http://example.com/">
<documentation><p>Commercial widgets.</p></documentation>
...
</resources>
</application>
Multi-segment Resource PathsIts a pain to require multiple nested <resources base="http://example.com/">
<resource uri="applications/catalogues/widgets">
...
</resource>
</resources>
instead of <resources base="http://example.com/">
<resource uri="applications">
<resource uri="catalogues">
<resource uri="widgets">
...
</resource>
</resource>
</resource>
</resources>
Much more compact and readable ! Matrix URI SupportThe <resource uri="widgets"> <path_variable name="maxPrice" type="xsd:decimal" matrix="true"/> <path_variable name="minPrice" type="xsd:decimal" matrix="true"/> ... </resource> If the value of widgets;maxPrice=10.00;minPrice=5.00 A null value for a matrix path variable results in it being omitted from the uri. Still on the Drawing BoardOther enhancements still on the drawing board include:
Mapping WADL to JavaPosted by mhadley on May 25, 2006 at 12:25 PM | Permalink | Comments (0)In recent entries I've described the REST (or Web Style) oriented features of JAX-WS and touched on a new language, WADL, that aims to provide a description of Web style services. In this entry I'll bring together these two threads to show how a language like WADL can be used to generate strongly-typed client-side stubs layered on JAX-WS and JAXB. One of the key differences between Web-style and the style commonly associated with WS-* is the inversion of the balance between Web resources (something identified by a URI) and methods that can be applied to those resources. In the former style you often find a single URI (or a port in WSDL terminology) that exposes many custom methods described by a WSDL port type. In the Web-style you find many URIs, each of which supports a subset of a few standard HTTP methods. The WS-* style maps quite naturally to a single Java class/interface per WSDL port type that exposes Java methods corresponding to each WSDL method. This is the approach taken by JAX-WS which describes in detail how to map between a WSDL port type and a Java class/interface (see chapters 2 and 3). When considering Web-style however, the mapping to Java is less obvious but it seems reasonable to try to preserve the hierarchical nature of HTTP URIs while maintaining the mapping between a Web resource and a Java class/interface. There are a couple of candidate hierarchical structures in the Java language: packages and nested inner classes. Given that the relationship between resources isn't always obvious it seems prudent to avoid member classes which require an instance of the parent class for instantiation. That leaves packages or nested static member classes, I chose the latter after a fair bit of back-and-forth but I think either approach would work. Lets look at an example. The following WADL file describes the Yahoo! News Search service: <?xml version="1.0"?>
<application xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:yn="urn:yahoo:yn"
xmlns:ya="urn:yahoo:api"
xmlns="http://research.sun.com/wadl">
<grammars>
<include href="NewsSearchResponse.xsd"/>
<include href="NewsSearchError.xsd"/>
</grammars>
<resources base="http://api.search.yahoo.com/NewsSearchService/V1/">
<resource uri="newsSearch">
<method href="#search"/>
</resource>
</resources>
<method name="GET" id="search">
<request>
<query_variable name="appid" type="xsd:string" required="true"/>
<query_variable name="query" type="xsd:string" required="true"/>
<query_variable name="type" type="xsd:string"/>
<query_variable name="results" type="xsd:int"/>
<query_variable name="start" type="xsd:int"/>
<query_variable name="sort" type="xsd:string"/>
<query_variable name="language" type="xsd:string"/>
</request>
<response>
<representation mediaType="application/xml" element="yn:ResultSet"/>
<fault id="SearchError" status="400" mediaType="application/xml"
element="ya:Error"/>
</response>
</method>
</application>
The service actually supports a few more optional query parameters but I've omitted those to keep the length of the example down. The WADL file describes a single resource ( package com.yahoo.search;
public class Endpoint {
...
public static class NewsSearch {
public NewsSearch()
throws JAXBException
{
...
}
public ResultSet getAsResultSet(String appid, String query)
throws SearchError
{
...
}
public ResultSet getAsResultSet(String appid, String query, String type,
Integer results, Integer start, String sort, String language)
throws SearchError
{
...
}
}
}
I've omitted the implementation code as I want to concentrate on the structure of the generated code rather than implementation details. The first thing to note is the top level class Once an instance of Notice also that both methods throw package com.yahoo.search;
public class SearchError
extends Exception
{
protected Error m_faultInfo;
public SearchError(String message, Error faultInfo) {
super(message);
m_faultInfo = faultInfo;
}
public Error getFaultInfo() {
return m_faultInfo;
}
}
where Using this approach we can write client code for the Yahoo! News Search service as follows: Endpoint.NewsSearch s = new Endpoint.NewsSearch();
ResultSet results = s.getAsResultSet("jaxws_restful_sample", "java");
for (ResultType result: results.getResult()) {
System.out.println(result.getTitle()+" ("+result.getClickUrl()+")");
}
Hopefully you'll agree that this is a distinct improvement when compared to using JAX-WS and JAXB without code generation though, as is often the case, you sacrifice ultimate flexibility and increase the coupling between client and server as the price for such convenience. I'm currently working on an implementation of a WADL to Java command line tool and Ant plug-in using the techniques described above. Its currently still a prototype but it produces working code for a variety of interesting WADL feature combinations and I hope to release something people can play with soon. In the meantime, if you are interested in this kind of tooling and/or have a WADL file you'd like me to add to my test set then please email me at firstname.lastname@sun.com (substituting the relevant values). JAX-WS and Binary DataPosted by mhadley on April 27, 2006 at 07:24 AM | Permalink | Comments (2)In previous entries I've shown how to use the JAX-WS APIs to publish and use RESTful Web services. Up till now I've focussed mainly on XML/HTTP since this is what most services use but JAX-WS can also handle other kinds of data. This entry shows how to use the JAX-WS APIs to work with binary image data and uses the popular Flickr service to illustrate the capabilities. The Flickr APIs come in three varieties: REST, XML-RPC and SOAP. We'll concentrate on the REST API for this entry and develop some code that will search a particular users photo stream using keywords. Before you start developing your own application you'll need to obtain an API key from Flickr; API keys are free for non-commercial use, I'll include the one I obtained in the code below but please get a new key for any new applications you might develop. The initial boilerplate code is the same as shown in previous URI baseURI = new URI("http://www.flickr.com/");
URI staticBaseURI = new URI("http://static.flickr.com/");
QName serviceName = new QName("FlickrService", baseURI.toString());
Service flickrService = Service.create(serviceName);
QName flickrPortName = new QName("flickr_port",baseURI.toString());
URI address = new URI("http", null, baseURI.getHost(), baseURI.getPort(),
baseURI.getPath()+"services/rest/", null, null);
flickrService.addPort(flickrPortName, HTTPBinding.HTTP_BINDING, address.toString());
Dispatch<Source> flickrDispatch = flickrService.createDispatch(flickrPortName,
Source.class, Service.Mode.PAYLOAD);
The Flickr photo search method takes a user ID rather than a user name so we first have to call the find by username method to convert the human-friendly // set up the method call and parameters
Map<String, Object> requestContext = flickrDispatch.getRequestContext();
requestContext.put(MessageContext.HTTP_REQUEST_METHOD, "GET");
String queryString = "method=flickr.people.findByUsername" +
"&api_key=f2b38aa733c997301cd7153f05d15410" +
"&username=" + java.net.URLEncoder.encode(username, "utf-8");
requestContext.put(MessageContext.QUERY_STRING, queryString);
// execute query
Source result = flickrDispatch.invoke(null);
DOMResult domResult = new DOMResult();
Transformer trans = TransformerFactory.newInstance().newTransformer();
trans.transform(result, domResult);
// parse results
XPathFactory xpf = XPathFactory.newInstance();
XPath xp = xpf.newXPath();
String userId = xp.evaluate("/rsp/user/@nsid", domResult.getNode());
With queryString = "method=flickr.photos.search" +
"&api_key=f2b38aa733c997301cd7153f05d15410" +
"&user_id=" + java.net.URLEncoder.encode(user, "utf-8") +
"&per_page=10" +
"&tags=" + java.net.URLEncoder.encode(tags, "utf-8");
requestContext.put(MessageContext.QUERY_STRING, queryString);
// execute query
result = flickrDispatch.invoke(null);
domResult = new DOMResult();
trans.transform(result, domResult);
// parse results
XPath xp = xpf.newXPath();
NodeList resultList = (NodeList)xp.evaluate("/rsp/photos/photo",
domResult.getNode(), XPathConstants.NODESET);
ArrayList<FlickrItem> list = new ArrayList<FlickrItem>();
for (int i=0;i<resultList.getLength();i++) {
String id = xp.evaluate("@id", resultList.item(i));
String title = xp.evaluate("@title", resultList.item(i));
String secret = xp.evaluate("@secret", resultList.item(i));
String server = xp.evaluate("@server", resultList.item(i));
FlickrItem item = new FlickrItem(staticBaseURI.toString(), id, server, secret, title);
list.add(item);
}
In the above I'm using a home-grown utility class public URL getMediumURL() {
return new URL(baseURI+server+"/"+id+"_"+secret+".jpg");
}
All of the above was really just a necessary precursor to get to what I really wanted to show: working with binary data using public Image getImage(FlickrItem item) {
URL url = item.getMediumURL();
Dispatch<DataSource> flickrStaticDispatch = flickrService.createDispatch(
flickrPortName, DataSource.class, Service.Mode.MESSAGE);
Map<String, Object> requestContext =
flickrStaticDispatch.getRequestContext();
requestContext.put(MessageContext.HTTP_REQUEST_METHOD, new String("GET"));
requestContext.put(Dispatch.ENDPOINT_ADDRESS_PROPERTY, url.toString());
DataSource result = flickrStaticDispatch.invoke(null);
BufferedImage img = ImageIO.read(result.getInputStream());
return img;
}
Note the use of Its a simple matter of programming to take the above code and turn it into an application like that shown below. If you'd like to see this working alongside other code samples from previous entries then join me at JavaOne on Tuesday 16th May at 3.15pm for my session on JAX-WS and RESTful Web Services.
WADL now a Sun Labs Technical ReportPosted by mhadley on April 26, 2006 at 02:45 PM | PermalinkThe Web Application Description Language (WADL) specification has now been published as a Sun Labs Technical Report (TR-153). RESTful Web Service Endpoints in JAX-WSPosted by mhadley on March 21, 2006 at 11:05 AM | Permalink | Comments (2)In previous entries I've covered use of the client side JAX-WS A JAX-WS public interface Provider<T> {
public T invoke(T request);
}
You also have to pick the service mode in which the endpoint will operate and annotate the endpoint class to allow the JAX-WS runtime to identify the correct class to publish. Here's the start of our endpoint class (I'm going to interleave code and narrative but if you extract and stitch together the code segments below you'll end up with a complete class): @WebServiceProvider
@ServiceMode(value=Service.Mode.PAYLOAD)
public class SystemPropertiesProvider implements Provider<Source> {
@Resource
protected WebServiceContext context;
Arun has an informative blog entry that discusses the difference between PAYLOAD and MESSAGE mode and describes which types of The endpoint we'll develop here will allow a client to query the Java system properties of the endpoint implementation class, here is the implementation of the public Source invoke(Source source) {
Map<String, List<String>> query = getQueryParameters();
String replyData = getSystemInfoAsXML(query.get("property"));
StreamSource reply = new StreamSource(new StringReader(replyData));
return reply;
}
The protected Map<String, List<String>> getQueryParameters() {
MessageContext msgCtx = context.getMessageContext();
String queryString = (String)msgCtx.get(MessageContext.QUERY_STRING);
HashMap<String, List<String>> params =
new HashMap<String, List<String>>();
for (String s: queryString.split("&")) {
String[] keyVal = s.split("=");
try {
String key = URLDecoder.decode(keyVal[0], "UTF-8");
String val = URLDecoder.decode(keyVal[1], "UTF-8");
if (params.get(key) == null) {
ArrayList<String> list = new ArrayList<String>();
list.add(val);
params.put(key,list);
}
else {
ArrayList<String> list =
(ArrayList<String>)params.get(key);
list.add(val);
}
} catch (UnsupportedEncodingException ex) {
ex.printStackTrace();
}
}
return params;
}
Its entirely possible that the above functionality already exists in some other class but, if it does, I didn't find it in the brief search I made. The protected String getSystemInfoAsXML(List The
If we wanted to deploy the endpoint in a Java EE container then the above is all the code needed. However, for this example, we'll use the new JAX-WS public static void main(String[] args) {
Endpoint e = Endpoint.create(HTTPBinding.HTTP_BINDING,
new SystemPropertiesProvider());
e.publish("http://127.0.0.1:8084/system/info");
System.out.println("Endpoint running");
try {
Thread.sleep(30000);
} catch (InterruptedException ex) {
}
e.stop();
System.out.println("Endpoint stopped");
}
}
In the above we publish the endpoint for 30 seconds and then stop it, you could also wait for a keypress in the main thread or use some other scheme to determine when to stop the endpoint. Now, stitch together the code segments from above, compile and run the class. Fire up a browser, point it to http://127.0.0.1:8084/system/info?property=os.version&property=os.name and see what OS the Java runtime thinks the endpoint is hosted on. Here's the XSL stylesheet that converts the XML document into a HTML table: <?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<html><body><table border="1">
<tr>
<th>Property</th>
<th>Value</th>
</tr>
<xsl:for-each select="/properties/property">
<tr>
<td>
<xsl:value-of select="@name"/>
</td>
<td>
<xsl:value-of select="."/>
</td>
</tr>
</xsl:for-each>
</table></body></html>
</xsl:template>
</xsl:stylesheet>
RESTful Web Services with JAX-WS and JAXBPosted by mhadley on March 08, 2006 at 11:44 AM | Permalink | Comments (1)In a previous blog entry I described how to use the dynamic client functionality of JAX-WS in combination with JAXP XPath capabilities to query a service providing data using XML/HTTP. In this entry I'd like to focus on how to use JAXB instead of XPath to get strongly typed access to the same data. To use JAXB we have to obtain an XML schema for the documents that will be exchanged. The documentation for the Yahoo News Search service includes a link to an XML schema for the results produced by the service. The JAXB tool time components transform an XML schema file into a set of Java classes that can be used to interact with an instance of a document that conforms to the schema. The JAXB runtime components take care of converting between the XML serialization of the data and its in-memory Java class equivalent. To integrate JAXB we have to run the
<property name="jaxb.home" value="/path/to/jwsdp-2.0/jaxb" />
<taskdef name="xjc" classname="com.sun.tools.xjc.XJCTask">
<classpath>
<fileset dir="${jaxb.home}" includes="lib/*.jar" />
</classpath>
</taskdef>
<target name="-pre-compile">
<echo message="Compiling the schema..." />
<xjc schema="NewsSearchResponse.xsd" package="com.yahoo.search" target="gen-src">
<produces dir="gen-src/com/yahoo/search" includes="**/*.java" />
</xjc>
</target>
Note that the above will produce a new directory separate to the application source code. You'll need to edit the NetBeans project preferences to include this directory as a source code directory to ensure the generated code is compiled into the project. As before the first step in the application is to add a part for the Yahoo News Search service:
URI nsURI = new URI("urn:yahoo:yn");
QName serviceName = new QName("yahoo",nsURI.toString());
QName portName = new QName("yahoo_port",nsURI.toString());
Service s = Service.create(serviceName);
URI address = new URI("http", null, "api.search.yahoo.com", 80,
"/NewsSearchService/V1/newsSearch",
"appid=jaxws_restful_sample&type=all&results=10&sort=date&query=java",
null);
s.addPort(portName, HTTPBinding.HTTP_BINDING, address.toString());
As before, note that the URI constructed above contains a set of input parameters to the search service. The appid parameter identifies the application to Yahoo, if you are writing a new application you should register a new ID for it here. In this instance we are creating a URI that will search for news about Java and return at most 10 results sorted by date. Once the port is created we can use the Service instance to create a Dispatch instance:
JAXBContext jbc = JAXBContext.newInstance( "com.yahoo.search" );
Dispatch<Object> d = s.createDispatch(portName, jbc, Service.Mode.PAYLOAD);
Map<String, Object> requestContext = d.getRequestContext();
requestContext.put(MessageContext.HTTP_REQUEST_METHOD, new String("GET"));
Note the following differences in the code compared the previous version that used the XPath APIs:
Now that we have a
ResultSet rs = (ResultSet)d.invoke(null);
for (ResultType r: rs.getResult()) {
System.out.printf("%s: (%s)\n",r.getTitle(),r.getClickUrl());
}
The Unfortunately, due to a bug in the JAX-WS implementation included in JWSDP 2.0, the above code will not work and requires the following work-around. I'm told this is fixed in the latest builds but haven't confirmed that personally.
Unmarshaller u = jbc.createUnmarshaller();
ResultSet rs = (ResultSet)u.unmarshal((StreamSource)d.invoke(null));
for (ResultType r: rs.getResult()) {
System.out.printf("%s: (%s)\n",r.getTitle(),r.getClickUrl());
}
RESTful Web Services with JAX-WSPosted by mhadley on January 10, 2006 at 01:58 PM | Permalink | Comments (7)The JAX-WS implementation that is included in the, recently released, JWSDP 2.0 preview now supports the XML/HTTP binding. This new functionality allows JAX-WS applications to implement and use RESTful Web services. Here is a worked example that demonstrates how to use JAX-WS to query the Yahoo News Search service. The first step is to create a JAX-WS port for the Yahoo News Search service
URI nsURI = new URI("urn:yahoo:yn");
QName serviceName = new QName("yahoo",nsURI.toString());
QName portName = new QName("yahoo_port",nsURI.toString());
Service s = Service.create(serviceName);
URI address = new URI("http", null, "api.search.yahoo.com", 80,
"/NewsSearchService/V1/newsSearch",
"appid=jaxws_restful_sample&type=all&results=10&sort=date&language=en&query=java",
null);
s.addPort(portName, HTTPBinding.HTTP_BINDING, address.toString());
Note that the URI constructed above contains a set of input parameters to the search service. The Once the port is created we can use the
Dispatch<Source> d = s.createDispatch(portName, Source.class, Service.Mode.PAYLOAD);
Map<String, Object> requestContext = d.getRequestContext();
requestContext.put(MessageContext.HTTP_REQUEST_METHOD, new String("GET"));
Now that we have a Source result = d.invoke(null); DOMResult domResult = new DOMResult(); Transformer trans = TransformerFactory.newInstance().newTransformer(); trans.transform(result, domResult); Now we have the results as a DOM tree we can apply XPath queries to extract the data we are interested in. Here we first use an XPath query to obtain the number of search results returned and then use that knowledge to interate through the results using additional queries.
XPathFactory xpf = XPathFactory.newInstance();
XPath xp = xpf.newXPath();
xp.setNamespaceContext(new NSResolver("yn", nsURI.toString()));
NodeList resultList = (NodeList)xp.evaluate("/yn:ResultSet/yn:Result", domResult.getNode(),
XPathConstants.NODESET);
int len = resultList.getLength();
for (int i=1;i<=len;i++) {
String title = xp.evaluate("/yn:ResultSet/yn:Result["+i+"]/yn:Title", domResult.getNode());
String click = xp.evaluate("/yn:ResultSet/yn:Result["+i+"]/yn:ClickUrl", domResult.getNode());
System.out.printf("[%d] %s (%s)\n",i,title,click);
}
The I mentioned above that there's no need to create a new JAX-WS port when the input data changes, the following lines submit a new query using the existing objects we've already created.
requestContext.put(MessageContext.QUERY_STRING,
"appid=jaxws_restful_sample&type=all&results=10&sort=date&language=en&query=solaris");
result = d.invoke(null);
That's it for now, in future blogs I'll demonstrate how to use JAXB instead of the XPath APIs to get strongly typed access to the results and how WADL can be used to generate JAX-WS based stubs for RESTful services. WADL RevisionPosted by mhadley on May 24, 2005 at 01:33 PM | Permalink | Comments (2)I got some useful feedback following the publication of the original version of the Web Application Description Language (WADL) and have revised the specification accordingly. Thanks particularly to Mark Nottingham and Philippe Le Hegaret for their useful comments, many of which are addressed in the latest version of the WADL specification (PDF) and WADL schema Changes since the previous version:
Still to do:
The W3C has just created a mailing list for discussion of Web description, if you have comments or thoughts about WADL or similarly targetted description languages then why not join the list and share your ideas. Introducing WADLPosted by mhadley on May 13, 2005 at 02:16 PM | Permalink | Comments (2)Seems like lots of folks are either doing it or talking about it (publishing proposals for alternatives to WSDL that is) so here's mine: Web Application Description Language (PDF) or WADL for short. A number of Web based enterprises (Google, Yahoo, Amazon, Flickr to name but a few) are developing XML/HTTP based applications/services that provide access to their internal data. Anecdotal evidence suggests there's a lot of simple XML/HTTP integration going on within other companies too. Add to this the recent interest in AJaX and you're left with a need for a simple way to describe such applications and services. WADL is designed to provide a simple alternative to WSDL for use with XML/HTTP Web applications. To date such applications have been mainly described using a combination of textual description and XML schema, WADL aims to provide a machine process-able description of such applications in a simpler format than is possible using WSDL.
Here's an example WADL description of the Yahoo news search application to illustrate the language: 01 <?xml version="1.0" standalone="yes"?> 02 <application targetNamespace="urn:yahoo:yn" 03 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 04 xmlns:xsd="http://www.w3.org/2001/XMLSchema" 05 xmlns:yn="urn:yahoo:yn" 06 xmlns:tns="urn:yahoo:yn" 07 xmlns:ya="urn:yahoo:api" 08 xmlns="http://research.sun.com/wadl"> 09 10 <types> 11 <include 12 href="http://.../NewsSearchService/V1/NewsSearchResponse.xsd"/> 13 <include 14 href="http://.../Api/V1/error.xsd"/> 15 </types> 16 17 <resources> 18 <resource uri="http://.../NewsSearchService/V1/newsSearch"> 19 <operationRef ref="tns:NewsSearch"/> 20 </resource> 21 </resources> 22 23 <operation name="NewsSearch" method="get"> 24 <request> 25 <parameter name="appid" type="xsd:string" required="true"/> 26 <parameter name="query" type="xsd:string" required="true"/> 27 <parameter name="type" type="xsd:string"/> 28 <parameter name="results" type="xsd:int"/> 29 <parameter name="start" type="xsd:int"/> 30 <parameter name="sort" type="xsd:string"/> 31 <parameter name="language" type="xsd:string"/> 32 </request> 33 <response> 34 <representation mediaType="text/xml" element="yn:ResultSet"> 35 <parameter name="totalResults" 36 type="xsd:nonNegativeInteger" 37 path="/ResultSet/@totalResultsAvailable"/> 38 <parameter name="resultsReturned" 39 type="xsd:nonNegativeInteger" 40 path="/ResultSet/@totalResultsReturned"/> 41 <parameter name="resultPosition" 42 type="xsd:nonNegativeInteger" 43 path="/ResultSet/@firstResultPosition"/> 44 <parameter name="results" path="/ResultSet/Result"/> 45 </representation> 46 <fault name="SearchError" status="400" 47 mediaType="text/xml" element="ya:Error"> 48 <parameter name="msg" path="/Error/Message" 49 type="xsd:string"/> 50 </fault> 51 </response> 52 </operation> 53</application> Lines 2-8 begin an application description and define the XML namespaces used elsewhere in the service description. Lines 10-15 define the XML schemas used by the service, in this case two schemas are included by reference. Lines 17-21 describe the Yahoo News Search Web resource and the operations it supports. Lines 23-52 describe the NewsSearch operation: lines 24-32 describe the inputs to the operation; lines 33-51 describe the possible outputs of the operation. The WADL specification (PDF) and WADL schema describe the features of the language in detail but a few points are worth highlighting:
I'm currently working on a WADL to Java compiler and will report back as things progress. Attachments for SOAPPosted by mhadley on August 26, 2004 at 09:33 AM | Permalink | Comments (5)This week has seen a bumper crop of specification releases in the area of SOAP attachments. On Tuesday WS-I released the final version of Attachments Profile 1.0 along with the associated Basic Profile 1.1 and Simple SOAP Binding Profile 1.0. On Thursday W3C released Candidate Recommendations of XML-binary Optimized Packaging (XOP), SOAP Message Transmission Optimization Mechanism (MTOM) and the Resource Representation SOAP Header Block. So, you may be asking yourself, what are the differences between the WS-I and W3C approaches and do I need to worry about them ? To answer the first part of the question one has to look at the underlying message models:
The serialized form of the two approaches is remarkably similar (there's only so many ways to boil an egg) but processing semantics differ. E.g. if I use XML signature to sign the insurance claim in the WS-I approach then I would actually be signing the reference to the photo rather than the photo bytes (WS-I is working on a solution to this in the Basic Security Working Group), if I do the same in the W3C approach then I would sign the base64 serialization of the photo (at the cost of having to do the base64 encoding that XOP/MTOM would otherwise avoid after all). To answer the second part of my question (do I need to worry about the differences): the JAX-RPC 2.0 (JSR 224) and JAXB 2.0 (JSR 222) Expert Groups are working hard to make sure that use of either mechanism is as transparent to Java developers as possible. These two specifications will support use of either approach and will hide as much of the complicated plumbing and conceptual doublethink as is practical. I expect the next drafts of both specifications to address attachments support. Java Web Services Developer Pack 1.4 ReleasedPosted by mhadley on June 24, 2004 at 06:47 PM | Permalink | Comments (1)The latest version of the Java Web Services Developer Pack (version 1.4) is now available and offers support for both the WS-I Basic Profile 1.1 and WS-I Attachments Profile 1.0. Support for the WS-I attachments profile now allows interoperable description and exchange of SOAP messages that include attachments based on the WSDL 1.1 MIME binding and the SOAP Messages With Attachments specification. In addition, the Java WSDP 1.4 also includes a full implementation of the OASIS Web Services Security (WSS) standards that provide message-level security for SOAP. This allows messages to be sent and stored securely independent of transport or storage security measures. The Java WSDP 1.4 provides developer choice and flexibility by supporting deployement on the following containers: the Sun Java System Application Server Platform Edition 8, the Sun Java System Web Server 6.1, and Tomcat 5.0 for Java WSDP 1.4. The Java WSDP 1.4 and supported Web containers are all available for free download.
JAX-RPC 2.0 Early DraftPosted by mhadley on June 23, 2004 at 06:37 PM | Permalink | Comments (0)It looks like I've already been scooped by Eduardo but just in case you missed that, the first early draft of JAX-RPC 2.0 is now available for review. It addresses the following goals and requirements:
Of these, integration wih JAXB is the most significant departure from previous versions of JAX-RPC. Earlier versions defined their own mapping between XML and Java data structures but in JAX-RPC 2.0 all data binding functionality is delegated to JAXB 2.0 while JAX-RPC still handles mapping between Java and WSDL constructs. This separation of responsibilities allows a developer to resuse code between JAXB and JAX-RPC based applications or to easily extend an existing JAXB based application to use JAX-RPC facilities. It also provides JAX-RPC with full support for XML Schema where previously only a subset was required to be supported. Other significant additions inlcude a new document-centric API for dynamic Web service invocation, support for dynamic and type-safe client side asynchrony and several improvements to the handler framework including protocol agnostic handlers. This early draft is still a work in progress, subsequent versions of the specification will address the following additional goals and requirements:
The expert group solicits your input on the current draft, details of where to send comments are included in the specification. W3C charters new XML Binary Characterization Working GroupPosted by mhadley on April 05, 2004 at 09:04 AM | Permalink | Comments (0)Following the successful W3C Workshop on Binary Interchange of XML Information Item Sets hosted by Sun Microsystems last year, the W3C has now chartered a new XML Binary Characterization Working Group to further investigate this fascinating area. From the WG home page: "The XML Binary Characterization Working Group is tasked with gathering information about uses cases where the overhead of generat | ||