| /* |
| * 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_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( 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); |
| } |
| |
| } |