/*
* FeedRepresentation.java
*
* Created on March 27, 2007, 11:55 PM
*
* To change this template, choose Tools | Template Manager
* and open the template in the editor.
*/
package org.atomojo.app;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.sql.SQLException;
import java.util.Date;
import java.util.Iterator;
import java.util.UUID;
import java.util.logging.Level;
import org.atomojo.app.auth.User;
import org.atomojo.app.db.Entry;
import org.atomojo.app.db.EntryMedia;
import org.atomojo.app.db.Feed;
import org.atomojo.app.util.Copy;
import org.atomojo.app.util.CopyFeedTask;
import org.atomojo.app.webdav.DAVCollection;
import org.atomojo.app.webdav.DAVResource;
import org.infoset.xml.Document;
import org.infoset.xml.DocumentLoader;
import org.infoset.xml.XMLException;
import org.infoset.xml.sax.SAXDocumentLoader;
import org.restlet.Application;
import org.restlet.data.CharacterSet;
import org.restlet.data.Form;
import org.restlet.data.MediaType;
import org.restlet.data.Reference;
import org.restlet.data.Status;
import org.restlet.data.Tag;
import org.restlet.representation.Representation;
import org.restlet.representation.StringRepresentation;
/**
*
* @author alex
*/
public class FeedResource extends AtomResource {
public static final String FEED_DOCUMENT_NAME = ".feed.atom";
/*
static class EntryDAVResource implements DAVResource {
String ref;
Entry entry;
public EntryDAVResource(String feedRef,Entry entry)
{
this.ref = feedRef+"_entry_."+entry.getUUID()+".atom";
this.entry = entry;
}
public String getReference() {
return ref;
}
public String getContentType() {
return "application/atom+xml";
}
public Date getLastModified() {
return entry.getEdited();
}
public Date getCreated() {
return entry.getCreated();
}
public long getContentLength() {
return -1;
}
public String getDisplayName() {
return ref;
}
}
*
*/
/*
static class MediaDAVResource implements DAVResource {
String ref;
EntryMedia media;
public MediaDAVResource(String feedRef,EntryMedia media)
{
this.ref = feedRef+media.getName();
this.media = media;
}
public String getReference() {
return ref;
}
public String getContentType() {
return media.getMediaType().getName();
}
public Date getLastModified() {
return media.getEdited();
}
public Date getCreated() {
return media.getCreated();
}
public long getContentLength() {
return -1;
}
public String getDisplayName() {
return media.getName();
}
}
*
*/
/*
class FeedDAVCollection implements DAVCollection {
String ref;
Feed davFeed;
public FeedDAVCollection(String ref,Feed davFeed)
{
this.ref = ref;
this.davFeed = davFeed;
}
public String getReference() {
return ref;
}
public String getContentType() {
return null;
}
public Date getLastModified() {
return davFeed.getEdited();
}
public Date getCreated() {
return davFeed.getCreated();
}
public long getContentLength() {
return -1;
}
public String getDisplayName() {
return davFeed.getName();
}
public Iterator<DAVResource> getChildren() {
try {
return new Iterator<DAVResource>() {
Iterator<Feed> children = davFeed.getChildren();
Iterator<Entry> entries = davFeed.getEntries();
Iterator<EntryMedia> resources = null;
Entry lastEntry = null;
public boolean hasNext() {
return children.hasNext() || entries.hasNext() || (resources!=null && resources.hasNext());
}
public DAVResource next() {
if (resources!=null) {
if (resources.hasNext()) {
EntryMedia media = resources.next();
return new MediaDAVResource(ref,media);
} else {
resources = null;
}
}
if (entries.hasNext()) {
Entry entry = entries.next();
try {
resources = entry.getResources();
return new EntryDAVResource(ref,entry);
} catch (SQLException ex) {
throw new RuntimeException("Cannot get entry resources for entry "+entry.getUUID()+" due to: "+ex.getMessage());
}
} else if (children.hasNext()) {
Feed childFeed = children.next();
String childRef = ref+childFeed.getName();
return new FeedDAVCollection(childRef,childFeed);
} else {
return null;
}
}
public void remove() {
throw new UnsupportedOperationException("Cannot remove from interator.");
}
};
} catch (SQLException ex) {
getLogger().log(Level.SEVERE,"Cannot get children of feed.",ex);
return null;
}
}
}
*
*/
Reference myself;
Storage store;
App app;
/** Creates a new instance of FeedRepresentation */
public FeedResource(Application theApp,Feed feed,Reference myself,Storage storage) {
super(theApp,feed,storage);
this.myself = myself;
this.app = new App(theApp.getContext().getLogger(),feed.getDB(),storage,theApp.getMetadataService());
}
public Representation get() {
Form parameters = getRequest().getResourceRef().getQueryAsForm();
try {
/*
if (getRequest().getConditions().getModifiedSince()!=null) {
getLogger().info("If-Modified-Since: "+AtomResource.toXSDDate(getRequest().getConditions().getModifiedSince()));
}
if (getRequest().getConditions().getUnmodifiedSince()!=null) {
getLogger().info("If-Unodified-Since: "+AtomResource.toXSDDate(getRequest().getConditions().getUnmodifiedSince()));
}
getLogger().info("Modified: "+AtomResource.toXSDDate(feed.getEdited()));
*/
String startS = parameters.getFirstValue("start");
String maxS = parameters.getFirstValue("max");
int start = startS==null ? 1 : Integer.parseInt(startS);
int max = maxS==null ? -1 : Integer.parseInt(maxS);
Representation rep = storage.getFeed(feed.getPath(),feed.getUUID(),feed.getEntries(start, max));
if (rep!=null) {
rep.setTag(new Tag(Long.toString(feed.getEdited().getTime()),false));
// Don't trust the storage to get the modification right
rep.setModificationDate(feed.getEdited());
rep.setExpirationDate(feed.getEdited());
}
getResponse().setStatus(Status.SUCCESS_OK);
return rep;
} catch (IOException ex) {
getContext().getLogger().log(Level.SEVERE,"Cannot get feed due to I/O exception.",ex);
getResponse().setStatus(Status.SERVER_ERROR_INTERNAL);
return new StringRepresentation("I/O exception while processing feed.");
} catch (SQLException ex) {
getContext().getLogger().log(Level.SEVERE,"Cannot get feed due to SQL exception.",ex);
getResponse().setStatus(Status.SERVER_ERROR_INTERNAL);
return new StringRepresentation("Database exception while processing feed.");
}
}
public String getTitle()
throws IOException
{
try {
return storage.getFeedTitle(feed.getPath(),feed.getUUID());
} catch (SQLException ex) {
throw new IOException("Exception while getting feed path: "+ex.getMessage());
}
}
public Representation post(Representation entity) {
MediaType postType = entity.getMediaType();
if (postType.getName().equals(MediaType.APPLICATION_ATOM.getName())) {
String charset = postType.getParameters().getValues("charset");
if (charset==null) {
charset = "UTF-8";
}
// Should be an entry
Document doc = null;
if (entity instanceof InfosetRepresentation) {
doc = ((InfosetRepresentation)entity).getDocument();
} else {
DocumentLoader loader = new SAXDocumentLoader();
try {
Reader r = new InputStreamReader(entity.getStream(),charset);
doc = loader.load(r);
r.close();
} catch (IOException ex) {
getResponse().setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
return new StringRepresentation("I/O error while parsing document: "+ex.getMessage());
} catch (XMLException ex) {
getResponse().setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
return new StringRepresentation("XML error while parsing document: "+ex.getMessage());
}
}
User user = (User)getRequest().getAttributes().get(App.USER_ATTR);
try {
Entry entry = app.createEntry(user,feed,doc);
getResponse().setStatus(Status.SUCCESS_CREATED);
Form headers = new Form();
getResponse().getAttributes().put("org.restlet.http.headers",headers);
Reference ref = new Reference(myself+"_/"+entry.getUUID().toString());
getResponse().setLocationRef(ref);
Representation rep = new InfosetRepresentation(MediaType.APPLICATION_ATOM,doc);
rep.setCharacterSet(CharacterSet.UTF_8);
rep.setTag(new Tag(Long.toString(entry.getEdited().getTime()),false));
MediaType entryType = new MediaType(rep.getMediaType().getName(),rep.getMediaType().getParameters().createSeries(EntryResource.entryParameters));
rep.setMediaType(entryType);
return rep;
} catch (AppException ex) {
getContext().getLogger().log(Level.SEVERE,ex.getMessage(),ex.getCause());
getResponse().setStatus(ex.getStatus());
return new StringRepresentation(ex.getMessage());
}
} else {
// Get the slug for the resource & entry
Form headers = (Form)getRequest().getAttributes().get("org.restlet.http.headers");
String slug = headers.getValues("slug");
if (slug==null) {
slug = headers.getValues("Slug");
}
if (slug!=null) {
try {
// Decode the precent encoding of the UTF-8 values
slug = URLDecoder.decode(slug,"UTF-8");
// Encode the slug as a URL encoding
slug = URLEncoder.encode(slug,"UTF-8");
} catch (UnsupportedEncodingException ex) {
getLogger().log(Level.SEVERE,"Cannot decode slug value: "+slug,ex);
}
getContext().getLogger().info("Slug="+slug);
}
String idS = headers.getValues("id");
UUID id = null;
if (idS!=null) {
try {
id = UUID.fromString(idS);
} catch (Exception ex) {
}
} else {
id = UUID.randomUUID();
}
try {
// Get author name for identity
User user = (User)getRequest().getAttributes().get(App.USER_ATTR);
Entry entry = app.createMediaEntry(user,feed,entity,slug,id);
getResponse().setStatus(Status.SUCCESS_CREATED);
headers = new Form();
getResponse().getAttributes().put("org.restlet.http.headers",headers);
Reference ref = new Reference(myself+"_/"+entry.getUUID().toString());
getResponse().setLocationRef(ref);
Representation rep = app.getEntryRepresentation(myself.toString(),entry);
rep.setCharacterSet(CharacterSet.UTF_8);
rep.setTag(new Tag(Long.toString(entry.getEdited().getTime()),false));
return rep;
} catch (AppException ex) {
if (ex.getStatus().getCode()==Status.SERVER_ERROR_INTERNAL.getCode()) {
getContext().getLogger().log(Level.SEVERE,ex.getMessage(),ex.getCause());
}
getResponse().setStatus(ex.getStatus());
return new StringRepresentation(ex.getMessage());
}
}
}
public Representation put(Representation entity) {
if (!entity.getMediaType().getName().equals(MediaType.APPLICATION_ATOM.getName())) {
getResponse().setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
return new StringRepresentation("Cannot put to an feed using media type "+entity.getMediaType());
}
String charset = entity.getMediaType().getParameters().getValues("charset");
if (charset==null) {
charset = "UTF-8";
}
try {
DocumentLoader loader = new SAXDocumentLoader();
Reader r = new InputStreamReader(entity.getStream(),charset);
Document doc = loader.load(r);
r.close();
try {
app.updateFeed(feed,doc);
getResponse().setStatus(Status.SUCCESS_NO_CONTENT);
return null;
} catch (AppException ex) {
if (ex.getStatus()==Status.SERVER_ERROR_INTERNAL) {
getContext().getLogger().log(Level.SEVERE,ex.getMessage(),ex.getCause());
}
getResponse().setStatus(ex.getStatus());
return new StringRepresentation(ex.getMessage());
}
} catch (XMLException ex) {
getResponse().setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
return new StringRepresentation("Cannot parse content: "+ex.getMessage());
} catch (Exception ex) {
getContext().getLogger().log(Level.SEVERE,"Fatal exception during post:"+ex.getMessage(),ex);
getResponse().setStatus(Status.SERVER_ERROR_INTERNAL);
return new StringRepresentation("Fatal error "+ex.getMessage());
}
}
public Representation delete() {
try {
app.delete(feed);
getResponse().setStatus(Status.SUCCESS_NO_CONTENT);
return null;
} catch (AppException ex) {
if (ex.getStatus()==Status.SERVER_ERROR_INTERNAL) {
getContext().getLogger().log(Level.SEVERE,ex.getMessage(),ex.getCause());
}
getResponse().setStatus(ex.getStatus());
return new StringRepresentation(ex.getMessage());
}
}
public Representation head() {
getResponse().setStatus(Status.SUCCESS_NO_CONTENT);
Representation rep = new StringRepresentation("",MediaType.APPLICATION_ATOM);
rep.setModificationDate(feed.getEdited());
rep.setTag(new Tag(Long.toString(feed.getEdited().getTime()),false));
return rep;
/*
Form headers = (Form)getResponse().getAttributes().get("org.restlet.http.headers");
if (headers==null) {
headers = new Form();
getResponse().getAttributes().put("org.restlet.http.headers",headers);
}
headers.add("DAV", "1");
*/
}
@Copy
public Representation copy() {
Form requestHeaders = (Form)getRequest().getAttributes().get("org.restlet.http.headers");
String dest = requestHeaders==null ? null : requestHeaders.getFirstValue("Destination");
if (dest==null) {
getResponse().setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
return null;
} else {
Reference ref = getRequest().getRootRef().clone().addSegment("R/");
User user = (User)getRequest().getAttributes().get(App.USER_ATTR);
URI root = URI.create(ref.toString());
String path = null;
try {
path = root.relativize(new URI(dest)).toString();
String [] segments = path.split("/");
Feed targetParent = feed.getDB().findFeedByPath(segments, 0, segments.length-1);
if (targetParent==null) {
getResponse().setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
return new StringRepresentation("Cannot find parent of destination "+dest);
}
getLogger().info("Copy from "+feed.getPath()+" to "+targetParent.getPath()+"/"+segments[segments.length-1]);
CopyFeedTask task = new CopyFeedTask(app,user,feed,targetParent,segments[segments.length-1]);
task.run();
if (task.getErrors().size()>0) {
StringBuilder builder = new StringBuilder();
for (String error : task.getErrors()) {
builder.append(error);
builder.append("\n");
}
getResponse().setStatus(Status.SERVER_ERROR_INTERNAL);
return new StringRepresentation(builder.toString());
} else {
getResponse().setStatus(Status.SUCCESS_NO_CONTENT);
return null;
}
} catch (SQLException ex) {
getLogger().log(Level.SEVERE,"Database error getting feed path "+path,ex);
getResponse().setStatus(Status.SERVER_ERROR_INTERNAL);
return new StringRepresentation("Database error.");
} catch (URISyntaxException ex) {
getResponse().setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
return new StringRepresentation("Bad destination URI "+dest+" : "+ex.getMessage());
}
}
}
/*
public void handleMove() {
Form requestHeaders = (Form)getRequest().getAttributes().get("org.restlet.http.headers");
String dest = requestHeaders==null ? null : requestHeaders.getFirstValue("Destination");
if (dest==null) {
getResponse().setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
} else {
Reference ref = getRequest().getRootRef().clone().addSegment("R/");
URI root = URI.create(ref.toString());
String path = null;
try {
path = root.relativize(new URI(dest)).toString();
String [] segments = path.split("/");
if (feed.getDB().findFeedByPath(segments)!=null) {
getResponse().setStatus(Status.CLIENT_ERROR_CONFLICT);
return;
}
} catch (SQLException ex) {
getLogger().log(Level.SEVERE,"Database error getting feed path "+path,ex);
getResponse().setStatus(Status.SERVER_ERROR_INTERNAL,"Database error.");
} catch (URISyntaxException ex) {
getResponse().setStatus(Status.CLIENT_ERROR_BAD_REQUEST,"Bad destination URI "+dest+" : "+ex.getMessage());
}
}
getResponse().setStatus(Status.SUCCESS_NO_CONTENT);
Representation rep = new StringRepresentation("",MediaType.APPLICATION_ATOM_XML);
rep.setModificationDate(feed.getEdited());
rep.setTag(new Tag(Long.toString(feed.getEdited().getTime()),false));
getResponse().setEntity(rep);
Form headers = (Form)getResponse().getAttributes().get("org.restlet.http.headers");
if (headers==null) {
headers = new Form();
getResponse().getAttributes().put("org.restlet.http.headers",headers);
}
headers.add("DAV", "1");
}
public void handleOptions() {
super.handleOptions();
Form headers = (Form)getResponse().getAttributes().get("org.restlet.http.headers");
if (headers==null) {
headers = new Form();
getResponse().getAttributes().put("org.restlet.http.headers",headers);
}
headers.add("DAV", "1");
}
public void handlePropfind() {
Form headers = (Form)getResponse().getAttributes().get("org.restlet.http.headers");
if (headers==null) {
headers = new Form();
getResponse().getAttributes().put("org.restlet.http.headers",headers);
}
headers.add("DAV", "1");
if (getRequest().isEntityAvailable()) {
Form requestHeaders = (Form)getRequest().getAttributes().get("org.restlet.http.headers");
String value = requestHeaders==null ? null : requestHeaders.getFirstValue("Depth");
final PropfindHandler propfind = new PropfindHandler(getLogger());
if ("1".equals(value)) {
getLogger().info("Request depth 1");
propfind.setRequestDepth(1);
} else if ("0".equals(value)) {
getLogger().info("Request depth 0");
propfind.setRequestDepth(0);
} else if ("infinity".equals(value)) {
getLogger().info("Request infinity");
propfind.setRequestDepth(-1);
}
try {
String xml = getRequest().getEntity().getText();
getLogger().info(xml);
propfind.loadRequest(new StringReader(xml));
} catch (Exception ex) {
getLogger().log(Level.SEVERE,"Cannot parse entity text.",ex);
}
getResponse().setStatus(Status.SUCCESS_MULTI_STATUS);
getResponse().setEntity(new OutputRepresentation(MediaType.APPLICATION_XML) {
public void write(OutputStream os)
throws IOException
{
Writer w = new OutputStreamWriter(os,"UTF-8");
propfind.handle(w,new FeedDAVCollection(myself.toString(),feed));
w.flush();
}
});
getResponse().getEntity().setCharacterSet(CharacterSet.UTF_8);
} else {
getResponse().setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
}
}
*/
}