blob: 4765370a9c50ac47c16b2f48bbbf900b330d4833 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.tuscany.sca.binding.atom.provider;
import static org.apache.tuscany.sca.binding.atom.provider.AtomBindingUtil.entry;
import static org.apache.tuscany.sca.binding.atom.provider.AtomBindingUtil.feedEntry;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.net.URLDecoder;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.StringTokenizer;
import java.util.logging.Logger;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.abdera.Abdera;
import org.apache.abdera.factory.Factory;
import org.apache.abdera.i18n.iri.IRI;
import org.apache.abdera.model.Category;
import org.apache.abdera.model.Collection;
import org.apache.abdera.model.Document;
import org.apache.abdera.model.Feed;
import org.apache.abdera.model.Link;
import org.apache.abdera.model.Service;
import org.apache.abdera.model.Workspace;
import org.apache.abdera.parser.ParseException;
import org.apache.abdera.parser.Parser;
import org.apache.abdera.writer.WriterFactory;
import org.apache.commons.codec.binary.Base64;
import org.apache.tuscany.sca.binding.http.HTTPCacheContext;
import org.apache.tuscany.sca.data.collection.Entry;
import org.apache.tuscany.sca.databinding.Mediator;
import org.apache.tuscany.sca.interfacedef.DataType;
import org.apache.tuscany.sca.interfacedef.Operation;
import org.apache.tuscany.sca.interfacedef.impl.DataTypeImpl;
import org.apache.tuscany.sca.invocation.InvocationChain;
import org.apache.tuscany.sca.invocation.Invoker;
import org.apache.tuscany.sca.invocation.Message;
import org.apache.tuscany.sca.invocation.MessageFactory;
import org.apache.tuscany.sca.runtime.RuntimeWire;
/**
* A resource collection binding listener, implemented as a Servlet and
* registered in a Servlet host provided by the SCA hosting runtime.
*
* @version $Rev$ $Date$
*/
class AtomBindingListenerServlet extends HttpServlet {
private static final Logger logger = Logger.getLogger(AtomBindingListenerServlet.class.getName());
private static final long serialVersionUID = 1L;
private static final Factory abderaFactory = Abdera.getNewFactory();
private static final Parser abderaParser = Abdera.getNewParser();
private static final String ETAG = "ETag";
private static final String LASTMODIFIED = "Last-Modified";
private static final String LOCATION = "Location";
private static final String CONTENTLOCATION = "Content-Location";
private static final SimpleDateFormat dateFormat = new SimpleDateFormat( "EEE, dd MMM yyyy HH:mm:ss Z" ); // RFC 822 date time
private RuntimeWire wire;
private Invoker getFeedInvoker;
private Invoker getAllInvoker;
private Invoker queryInvoker;
private Invoker getInvoker;
private Invoker postInvoker;
private Invoker postMediaInvoker;
private Invoker putInvoker;
private Invoker putMediaInvoker;
private Invoker deleteInvoker;
private MessageFactory messageFactory;
private String title;
private String description;
private Mediator mediator;
private DataType<?> itemClassType;
private DataType<?> itemXMLType;
private boolean supportsFeedEntries;
/**
* Constructs a new binding listener.
*
* @param wire
* @param messageFactory
* @param feedType
*/
AtomBindingListenerServlet(RuntimeWire wire, MessageFactory messageFactory, Mediator mediator, String title, String description) {
this.wire = wire;
this.messageFactory = messageFactory;
this.mediator = mediator;
this.title = title;
this.description = description;
// Get the invokers for the supported operations
Operation getOperation = null;
for (InvocationChain invocationChain : this.wire.getInvocationChains()) {
invocationChain.setAllowsPassByReference(true);
Operation operation = invocationChain.getTargetOperation();
String operationName = operation.getName();
if (operationName.equals("getFeed")) {
getFeedInvoker = invocationChain.getHeadInvoker();
} else if (operationName.equals("getAll")) {
getAllInvoker = invocationChain.getHeadInvoker();
} else if (operationName.equals("query")) {
queryInvoker = invocationChain.getHeadInvoker();
} else if (operationName.equals("get")) {
getInvoker = invocationChain.getHeadInvoker();
getOperation = operation;
} else if (operationName.equals("put")) {
putInvoker = invocationChain.getHeadInvoker();
} else if (operationName.equals("putMedia")) {
putMediaInvoker = invocationChain.getHeadInvoker();
} else if (operationName.equals("post")) {
postInvoker = invocationChain.getHeadInvoker();
} else if (operationName.equals("postMedia")) {
postMediaInvoker = invocationChain.getHeadInvoker();
} else if (operationName.equals("delete")) {
deleteInvoker = invocationChain.getHeadInvoker();
}
}
// Determine the collection item type
if (getOperation != null) {
itemXMLType = new DataTypeImpl<Class<?>>(String.class.getName(), String.class, String.class);
Class<?> itemClass = getOperation.getOutputType().getPhysical();
if (itemClass == org.apache.abdera.model.Entry.class) {
supportsFeedEntries = true;
}
//We assume that the item type is the same for both input and
//ouput for all operations on the interface
itemClassType = getOperation.getOutputType();
}
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// No authentication required for a get request
// Test for any cache info in the request
HTTPCacheContext cacheContext = null;
try {
cacheContext = HTTPCacheContext.getCacheContextFromRequest( request );
} catch ( java.text.ParseException e ) {
}
// System.out.println( "AtomBindingListener.doGet cache context=" + cacheContext );
// Get the request path
//int servletPathLength = request.getContextPath().length() + request.getServletPath().length();
//String path = URLDecoder.decode(request.getRequestURI().substring(servletPathLength), "UTF-8");
String path = URLDecoder.decode(getRequestPath(request), "UTF-8");
logger.fine("get " + request.getRequestURI());
// Handle an Atom request
if (path != null && path.equals("/atomsvc")) {
/*
<?xml version='1.0' encoding='UTF-8'?>
<service xmlns="http://www.w3.org/2007/app" xmlns:atom="http://www.w3.org/2005/Atom">
<workspace>
<atom:title type="text">resource</atom:title>
<collection href="http://luck.ibm.com:8084/customer">
<atom:title type="text">entries</atom:title>
<accept>application/atom+xml;type=entry</accept>
<categories />
</collection>
</workspace>
</service>
*/
// Return the Atom service document
response.setContentType("application/atomsvc+xml; charset=utf-8");
String href = request.getRequestURL().toString();
href = href.substring(0, href.length() - "/atomsvc".length());
String workspaceURL = new String( href );
int pathIndex = workspaceURL.indexOf( request.getServletPath() );
if ( -1 != pathIndex ) {
workspaceURL = workspaceURL.substring( 0, pathIndex ) + "/";
}
Service service = abderaFactory.newService();
//service.setText("service");
Workspace workspace = abderaFactory.newWorkspace();
if ( title != null ) {
workspace.setTitle(title);
} else {
workspace.setTitle("workspace");
}
workspace.setBaseUri( new IRI( workspaceURL ));
Collection collection = workspace.addCollection("collection", href );
Feed feed = getFeed( request );
if ( feed != null ) {
String title = feed.getTitle();
if ( title != null ) {
collection.setTitle(title);
} else {
collection.setTitle("entries");
}
collection.addAccepts("application/atom+xml;type=feed");
collection.addAccepts("application/json;type=feed");
collection.addAccepts("application/atom+xml;type=entry");
collection.addAccepts("application/json;type=entry");
List<Category> categories = feed.getCategories();
if ( categories != null ) {
collection.addCategories(categories, false, null);
} else {
collection.addCategories().setFixed(false);
}
} else {
collection.setTitle("entries");
// collection.addAccepts("application/atom+xml;type=feed");
collection.addAccepts("application/atom+xml; type=entry");
collection.addAccepts("application/json;type=entry");
collection.addCategories().setFixed(false);
}
workspace.addCollection(collection);
service.addWorkspace(workspace);
//FIXME add prettyPrint support
try {
service.getDocument().writeTo(response.getOutputStream());
} catch (IOException ioe) {
throw new ServletException(ioe);
}
} else if (path == null || path.length() == 0 || path.equals("/")) {
// Return a feed containing the entries in the collection
Feed feed = getFeed( request );
if (feed != null) {
String feedETag = null;
if (feed.getId() != null)
feedETag = "\"" + feed.getId().toString() + "\"";
Date feedUpdated = feed.getUpdated();
// Test request for predicates.
String predicate = request.getHeader( "If-Match" );
if (( predicate != null ) && ( !predicate.equals(feedETag) )) {
// No match, should short circuit
response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
return;
}
predicate = request.getHeader( "If-None-Match" );
if (( predicate != null ) && ( predicate.equals(feedETag) )) {
// Match, should short circuit
response.sendError(HttpServletResponse.SC_NOT_MODIFIED);
return;
}
if ( feedUpdated != null ) {
predicate = request.getHeader( "If-Unmodified-Since" );
if ( predicate != null ) {
try {
Date predicateDate = dateFormat.parse( predicate );
if ( predicateDate.compareTo( exactSeconds(feedUpdated) ) < 0 ) {
// Match, should short circuit
response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
return;
}
} catch ( java.text.ParseException e ) {
// Ignore and move on
}
}
predicate = request.getHeader( "If-Modified-Since" );
if ( predicate != null ) {
try {
Date predicateDate = dateFormat.parse( predicate );
if ( predicateDate.compareTo( exactSeconds(feedUpdated) ) >= 0 ) {
// Match, should short circuit
response.sendError(HttpServletResponse.SC_NOT_MODIFIED);
return;
}
} catch ( java.text.ParseException e ) {
// Ignore and move on
}
}
}
// Provide Etag based on Id and time if given.
// Ignore if not given. (Browser may cache if trivial ETag is given.)
if ( feedETag != null ) {
response.addHeader(ETAG, feedETag);
}
if ( feedUpdated != null ) {
response.addHeader(LASTMODIFIED, dateFormat.format( feedUpdated ));
}
// Content negotiation
String acceptType = request.getHeader( "Accept" );
String preferredType = getContentPreference( acceptType );
if (( preferredType != null ) && ((preferredType.indexOf( "json") > -1) || (preferredType.indexOf( "JSON") > -1 ))) {
// JSON response body
response.setContentType("application/json;type=feed");
try {
Abdera abdera = new Abdera();
WriterFactory wf = abdera.getWriterFactory();
org.apache.abdera.writer.Writer json = wf.getWriter("json");
feed.writeTo(json, response.getWriter());
} catch (Exception e) {
throw new ServletException(e);
}
} else {
// Write the Atom feed
response.setContentType("application/atom+xml;type=feed");
try {
feed.getDocument().writeTo(response.getOutputStream());
} catch (IOException ioe) {
throw new ServletException(ioe);
}
}
} else {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
}
} else if (path.startsWith("/")) {
// Return a specific entry in the collection
org.apache.abdera.model.Entry feedEntry;
// Invoke the get operation on the service implementation
Message requestMessage = messageFactory.createMessage();
String id = path.substring(1);
requestMessage.setBody(new Object[] {id});
Message responseMessage = getInvoker.invoke(requestMessage);
if (responseMessage.isFault()) {
Object body = responseMessage.getBody();
if (body.getClass().getName().endsWith(".NotFoundException")) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
} else {
throw new ServletException((Throwable)responseMessage.getBody());
}
}
if (supportsFeedEntries) {
// The service implementation returns a feed entry
feedEntry = responseMessage.getBody();
} else {
// The service implementation only returns a data item, create an entry
// from it
Entry<Object, Object> entry = new Entry<Object, Object>(id, responseMessage.getBody());
feedEntry = feedEntry(entry, itemClassType, itemXMLType, mediator, abderaFactory);
}
// Write the Atom entry
if (feedEntry != null) {
String entryETag = null;
if (feedEntry.getId() != null)
entryETag = feedEntry.getId().toString();
Date entryUpdated = feedEntry.getUpdated();
if ( entryUpdated != null )
response.addHeader(LASTMODIFIED, dateFormat.format( entryUpdated ));
// TODO Check If-Modified-Since If-Unmodified-Since predicates against LASTMODIFIED.
// If true return 304 and null body.
Link link = feedEntry.getSelfLink();
if (link != null) {
response.addHeader(LOCATION, link.getHref().toString());
} else {
link = feedEntry.getLink( "Edit" );
if (link != null) {
response.addHeader(LOCATION, link.getHref().toString());
}
}
// Test request for predicates.
String predicate = request.getHeader( "If-Match" );
if (( predicate != null ) && ( !predicate.equals(entryETag) )) {
// No match, should short circuit
response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
return;
}
predicate = request.getHeader( "If-None-Match" );
if (( predicate != null ) && ( predicate.equals(entryETag) )) {
// Match, should short circuit
response.sendError(HttpServletResponse.SC_NOT_MODIFIED);
return;
}
if ( entryUpdated != null ) {
predicate = request.getHeader( "If-Unmodified-Since" );
if ( predicate != null ) {
try {
Date predicateDate = dateFormat.parse( predicate );
if ( predicateDate.compareTo( entryUpdated ) < 0 ) {
// Match, should short circuit
response.sendError(HttpServletResponse.SC_NOT_MODIFIED);
return;
}
} catch ( java.text.ParseException e ) {
// Ignore and move on
}
}
predicate = request.getHeader( "If-Modified-Since" );
if ( predicate != null ) {
try {
Date predicateDate = dateFormat.parse( predicate );
if ( predicateDate.compareTo( entryUpdated ) > 0 ) {
// Match, should short circuit
response.sendError(HttpServletResponse.SC_NOT_MODIFIED);
return;
}
} catch ( java.text.ParseException e ) {
// Ignore and move on
}
}
}
// Provide Etag based on Id and time if given.
// Ignore if not given. (Browser may cache if trivial ETag is given.)
if (entryETag != null) {
response.addHeader(ETAG, entryETag );
}
if ( entryUpdated != null ) {
response.addHeader(LASTMODIFIED, dateFormat.format( entryUpdated ));
}
// Content negotiation
String acceptType = request.getHeader( "Accept" );
String preferredType = getContentPreference( acceptType );
if (( preferredType != null ) && ((preferredType.indexOf( "json") > -1) || (preferredType.indexOf( "JSON") > -1 ))) {
// JSON response body
response.setContentType("application/json;type=entry");
try {
Abdera abdera = new Abdera();
WriterFactory wf = abdera.getWriterFactory();
org.apache.abdera.writer.Writer json = wf.getWriter("json");
feedEntry.writeTo(json, response.getWriter());
} catch (Exception e) {
throw new ServletException(e);
}
} else {
// XML response body
response.setContentType("application/atom+xml;type=entry");
try {
feedEntry.writeTo(getWriter(response));
} catch (IOException ioe) {
throw new ServletException(ioe);
}
}
} else {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
}
} else {
// Path doesn't match any known pattern
response.sendError(HttpServletResponse.SC_NOT_FOUND);
}
}
protected Feed getFeed( HttpServletRequest request ) throws ServletException {
if (supportsFeedEntries) {
// The service implementation supports feed entries, invoke its getFeed operation
Message requestMessage = messageFactory.createMessage();
Message responseMessage;
if (request.getQueryString() != null) {
requestMessage.setBody(new Object[] {request.getQueryString()});
responseMessage = queryInvoker.invoke(requestMessage);
} else {
responseMessage = getFeedInvoker.invoke(requestMessage);
}
if (responseMessage.isFault()) {
throw new ServletException((Throwable)responseMessage.getBody());
}
return (Feed)responseMessage.getBody();
} else {
// The service implementation does not support feed entries,
// invoke its getAll operation to get the data item collection, then create
// feed entries from the items
Message requestMessage = messageFactory.createMessage();
Message responseMessage;
if (request.getQueryString() != null) {
requestMessage.setBody(new Object[] {request.getQueryString()});
responseMessage = queryInvoker.invoke(requestMessage);
} else {
responseMessage = getAllInvoker.invoke(requestMessage);
}
if (responseMessage.isFault()) {
throw new ServletException((Throwable)responseMessage.getBody());
}
Entry<Object, Object>[] collection = (Entry<Object, Object>[])responseMessage.getBody();
if (collection != null) {
// Create the feed
Feed feed = abderaFactory.newFeed();
// Set the feed title
if (title != null) {
feed.setTitle(title);
} else {
feed.setTitle("Feed");
}
if (description != null) {
feed.setSubtitle(description);
}
// All feeds must provide Id and updated elements.
// However, some do not, so provide some program protection.
feed.setId( "Feed" + feed.hashCode());
Date responseLastModified = new Date( 0 );
// Add entries to the feed
for (Entry<Object, Object> entry: collection) {
org.apache.abdera.model.Entry feedEntry = feedEntry(entry, itemClassType, itemXMLType, mediator, abderaFactory);
// Use the most recent entry update as the feed update
Date entryUpdated = feedEntry.getUpdated();
if (( entryUpdated != null ) && (entryUpdated.compareTo( responseLastModified ) > 0 )) {
responseLastModified = entryUpdated;
}
feed.addEntry(feedEntry);
}
// If no entries were newly updated,
if ( responseLastModified.compareTo( new Date( 0 ) ) == 0 ) {
responseLastModified = new Date();
}
return feed;
}
}
return null;
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException,
IOException {
// Authenticate the user
String user = processAuthorizationHeader(request);
if (user == null) {
unauthorized(response);
return;
}
// Get the request path
String path = URLDecoder.decode(getRequestPath(request), "UTF-8");
if (path == null || path.length() == 0 || path.equals("/")) {
org.apache.abdera.model.Entry createdFeedEntry = null;
// Create a new Atom entry
String contentType = request.getContentType();
if (contentType != null && contentType.startsWith("application/atom+xml")) {
// Read the entry from the request
org.apache.abdera.model.Entry feedEntry;
try {
Document<org.apache.abdera.model.Entry> doc = abderaParser.parse(request.getReader());
feedEntry = doc.getRoot();
} catch (ParseException pe) {
throw new ServletException(pe);
}
// Let the component implementation create it
if (supportsFeedEntries) {
// The service implementation supports feed entries, pass the entry to it
Message requestMessage = messageFactory.createMessage();
requestMessage.setBody(new Object[] {feedEntry});
Message responseMessage = postInvoker.invoke(requestMessage);
if (responseMessage.isFault()) {
throw new ServletException((Throwable)responseMessage.getBody());
}
createdFeedEntry = responseMessage.getBody();
} else {
// The service implementation does not support feed entries, pass the data item to it
Message requestMessage = messageFactory.createMessage();
Entry<Object, Object> entry = entry(feedEntry, itemClassType, itemXMLType, mediator);
requestMessage.setBody(new Object[] {entry.getKey(), entry.getData()});
Message responseMessage = postInvoker.invoke(requestMessage);
if (responseMessage.isFault()) {
throw new ServletException((Throwable)responseMessage.getBody());
}
entry.setKey(responseMessage.getBody());
createdFeedEntry = feedEntry(entry, itemClassType, itemXMLType, mediator, abderaFactory);
}
} else if (contentType != null) {
// Create a new media entry
// Get incoming headers
String title = request.getHeader("Title");
String slug = request.getHeader("Slug");
// Let the component implementation create the media entry
Message requestMessage = messageFactory.createMessage();
requestMessage.setBody(new Object[] {title, slug, contentType, request.getInputStream()});
Message responseMessage = postMediaInvoker.invoke(requestMessage);
if (responseMessage.isFault()) {
throw new ServletException((Throwable)responseMessage.getBody());
}
createdFeedEntry = responseMessage.getBody();
// Transfer media info to response header.
// Summary is a comma separated list of header properties.
String summary = createdFeedEntry.getSummary();
addPropertiesToHeader( response, summary );
} else {
response.sendError(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);
}
// A new entry for non-media was created successfully.
if (createdFeedEntry != null) {
// Set location of the created entry in the Location header
IRI feedId = createdFeedEntry.getId();
if ( feedId != null ) {
response.addHeader(ETAG, "\"" + feedId.toString() + "\"" );
}
Date entryUpdated = createdFeedEntry.getUpdated();
if ( entryUpdated != null ) {
response.addHeader(LASTMODIFIED, dateFormat.format( entryUpdated ));
}
Link link = createdFeedEntry.getSelfLink();
if (link != null) {
response.addHeader(LOCATION, link.getHref().toString());
}
Link editLink = createdFeedEntry.getEditLink();
if (editLink != null) {
response.addHeader(LOCATION, editLink.getHref().toString());
}
Link editMediaLink = createdFeedEntry.getEditMediaLink();
if (editMediaLink != null) {
response.addHeader(CONTENTLOCATION, editMediaLink.getHref().toString());
}
// Write the created Atom entry
response.setStatus(HttpServletResponse.SC_CREATED);
response.setContentType("application/atom+xml;type=entry");
try {
createdFeedEntry.writeTo(getWriter(response));
} catch (ParseException pe) {
throw new ServletException(pe);
}
} else {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
}
} else {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
}
}
private Writer getWriter(HttpServletResponse response) throws UnsupportedEncodingException, IOException {
Writer writer = new OutputStreamWriter(response.getOutputStream(), "UTF-8");
return writer;
}
@Override
protected void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// Authenticate the user
String user = processAuthorizationHeader(request);
if (user == null) {
unauthorized(response);
return;
}
// Get the request path
String path = URLDecoder.decode(getRequestPath(request), "UTF-8");
if (path != null && path.startsWith("/")) {
String id = path.substring(1);
// Update an Atom entry
String contentType = request.getContentType();
if (contentType != null && contentType.startsWith("application/atom+xml")) {
// Read the entry from the request
org.apache.abdera.model.Entry feedEntry;
try {
Document<org.apache.abdera.model.Entry> doc = abderaParser.parse(request.getReader());
feedEntry = doc.getRoot();
} catch (ParseException pe) {
throw new ServletException(pe);
}
// Let the component implementation create it
if (supportsFeedEntries) {
// The service implementation supports feed entries, pass the entry to it
Message requestMessage = messageFactory.createMessage();
requestMessage.setBody(new Object[] {id, feedEntry});
Message responseMessage = putInvoker.invoke(requestMessage);
if (responseMessage.isFault()) {
Object body = responseMessage.getBody();
if (body.getClass().getName().endsWith(".NotFoundException")) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
} else {
throw new ServletException((Throwable)responseMessage.getBody());
}
}
} else {
// The service implementation does not support feed entries, pass the data item to it
Message requestMessage = messageFactory.createMessage();
Entry<Object, Object> entry = entry(feedEntry, itemClassType, itemXMLType, mediator);
requestMessage.setBody(new Object[] {entry.getKey(), entry.getData()});
Message responseMessage = putInvoker.invoke(requestMessage);
if (responseMessage.isFault()) {
Object body = responseMessage.getBody();
if (body.getClass().getName().endsWith(".NotFoundException")) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
} else {
throw new ServletException((Throwable)responseMessage.getBody());
}
}
}
} else if (contentType != null) {
// Update a media entry
// Let the component implementation create the media entry
Message requestMessage = messageFactory.createMessage();
requestMessage.setBody(new Object[] {id, contentType, request.getInputStream()});
Message responseMessage = putMediaInvoker.invoke(requestMessage);
Object body = responseMessage.getBody();
if (responseMessage.isFault()) {
if (body.getClass().getName().endsWith(".NotFoundException")) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
} else {
throw new ServletException((Throwable)responseMessage.getBody());
}
}
// Transfer content to response header.
} else {
response.sendError(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);
}
} else {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
}
}
@Override
protected void doDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// Authenticate the user
String user = processAuthorizationHeader(request);
if (user == null) {
unauthorized(response);
return;
}
// Get the request path
String path = URLDecoder.decode(getRequestPath(request), "UTF-8");
String id;
if (path != null && path.startsWith("/")) {
id = path.substring(1);
} else {
id = "";
}
// Delete a specific entry from the collection
Message requestMessage = messageFactory.createMessage();
requestMessage.setBody(new Object[] {id});
Message responseMessage = deleteInvoker.invoke(requestMessage);
if (responseMessage.isFault()) {
Object body = responseMessage.getBody();
if (body.getClass().getName().endsWith(".NotFoundException")) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
} else {
throw new ServletException((Throwable)responseMessage.getBody());
}
}
}
/**
*
* @param request
* @return
*/
private static String getRequestPath(HttpServletRequest request) {
// Get the request path
String contextPath = request.getContextPath();
String servletPath = request.getServletPath();
String requestURI = request.getRequestURI();
int contextPathLength = request.getContextPath().length();
int servletPathLenght = servletPath.contains(contextPath) ? servletPath.length() - contextPath.length() : servletPath.length();
String requestPath = requestURI.substring(contextPathLength + servletPathLenght);
return requestPath;
}
/**
* Process the authorization header
*
* @param request
* @return
* @throws ServletException
*/
private String processAuthorizationHeader(HttpServletRequest request) throws ServletException {
// FIXME temporarily disabling this as it doesn't work with all browsers
if (true)
return "admin";
try {
String authorization = request.getHeader("Authorization");
if (authorization != null) {
StringTokenizer tokens = new StringTokenizer(authorization);
if (tokens.hasMoreTokens()) {
String basic = tokens.nextToken();
if (basic.equalsIgnoreCase("Basic")) {
String credentials = tokens.nextToken();
String userAndPassword = new String(Base64.decodeBase64(credentials.getBytes()));
int colon = userAndPassword.indexOf(":");
if (colon != -1) {
String user = userAndPassword.substring(0, colon);
String password = userAndPassword.substring(colon + 1);
// Authenticate the User.
if (authenticate(user, password)) {
return user;
}
}
}
}
}
} catch (Exception e) {
throw new ServletException(e);
}
return null;
}
/**
* Authenticate a user.
*
* @param user
* @param password
* @return
*/
private boolean authenticate(String user, String password) {
// TODO Handle this using SCA security policies
return ("admin".equals(user) && "admin".equals(password));
}
/**
* Reject an unauthorized request.
*
* @param response
*/
private void unauthorized(HttpServletResponse response) throws IOException {
response.setHeader("WWW-Authenticate", "BASIC realm=\"Tuscany\"");
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
}
/**
* Get content-type preference using application/atom-xml as default
* @param acceptType content-type preference using application/atom-xml as default
* @return
*/
private static String getContentPreference( String acceptType ) {
if (( acceptType == null ) || ( acceptType.length() < 1 )) {
return "application/atom+xml";
}
StringTokenizer st = new StringTokenizer( acceptType, "," );
if ( st.hasMoreTokens() ) {
return st.nextToken();
}
return "application/atom+xml";
}
/** Take a list of key values and add them to the header.
* For instance "Content-Type=image/gif,Content-Length=14201"
* @param response
* @param properties
*/
private static void addPropertiesToHeader( HttpServletResponse response, String properties ) {
if ( properties == null ) return;
StringTokenizer props = new StringTokenizer( properties, ",");
while( props.hasMoreTokens()) {
String prop = props.nextToken();
StringTokenizer keyVal = new StringTokenizer( prop, "=");
String key = null;
String val = null;
if ( keyVal.hasMoreTokens() )
key = keyVal.nextToken();
if ( keyVal.hasMoreTokens() )
val = keyVal.nextToken();
if (( key != null ) && ( val != null )) {
response.addHeader(key, val);
}
}
}
/**
* Round a date down to an exact number of seconds
* @param date with millisecond precision
* @return date rounded down to nearest second
*/
private Date exactSeconds(Date date) {
return new Date(date.getTime() / 1000 * 1000);
}
}