|
|
|||||||||||||||||||||||||||||||||||||||||||||
Marc Hadley's Blog
JAX-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 (3)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. |
May 2008
Search this blog:CategoriesCommunity: Java Web Services and XMLCommunity: Mac Java Community Distributed J2EE J2SE Programming Web Services and XML Archives
May 2008 Recent EntriesJAX-RS Public Review Draft and JavaOne | ||||||||||||||||||||||||||||||||||||||||||||
|
|