/*
* 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.StringReader;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
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.DB.MediaEntryListener;
import org.atomojo.app.db.Entry;
import org.atomojo.app.db.Feed;
import org.atomojo.app.db.EntryMedia;
import org.atomojo.app.util.Copy;
import org.atomojo.app.util.Move;
import org.infoset.xml.Document;
import org.infoset.xml.DocumentLoader;
import org.infoset.xml.Element;
import org.infoset.xml.sax.SAXDocumentLoader;
import org.restlet.Application;
import org.restlet.data.CharacterSet;
import org.restlet.data.Form;
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 MediaResource extends AtomResource {
EntryMedia media;
App app;
/** Creates a new instance of FeedRepresentation */
public MediaResource(Application app,Feed feed,EntryMedia media,Storage storage) {
super(app,feed,storage);
this.media = media;
this.app = new App(getContext().getLogger(),feed.getDB(),storage,theApp.getMetadataService());
}
public EntryMedia getMedia() {
return media;
}
public Representation get() {
try {
Date modifiedSince = getRequest().getConditions().getUnmodifiedSince();
if (modifiedSince!=null) {
getLogger().info("Modified since "+modifiedSince+", edited="+media.getEdited());
}
if (modifiedSince==null || modifiedSince.getTime()<media.getEdited().getTime()) {
Representation rep = storage.getMedia(feed.getPath(),feed.getUUID(),media.getName());
rep.setMediaType(media.getMediaType());
getResponse().setStatus(Status.SUCCESS_OK);
return rep;
} else {
getResponse().setStatus(Status.REDIRECTION_NOT_MODIFIED);
return null;
}
} catch (IOException ex) {
getContext().getLogger().log(Level.SEVERE,"Cannot get media: "+ex.getMessage(),ex);
getResponse().setStatus(Status.SERVER_ERROR_INTERNAL);
return new StringRepresentation("Cannot retrieve media.");
} catch (SQLException ex) {
getContext().getLogger().log(Level.SEVERE,"Cannot get media: "+ex.getMessage(),ex);
getResponse().setStatus(Status.SERVER_ERROR_INTERNAL);
return new StringRepresentation("Cannot retrieve media.");
}
}
public Representation put(Representation entity) {
try {
media.setMediaType(entity.getMediaType());
Status status = storage.storeMedia(feed.getPath(),feed.getUUID(),media.getName(),entity.getMediaType(),entity.getStream());
if (!status.isSuccess()) {
getResponse().setStatus(status);
return new StringRepresentation("Cannot store media.");
}
media.touch();
// get the media resoruce's entry
Entry entry = media.getEntry();
// Get the entry's representation
String feedBaseURI = getRequest().getResourceRef().getParentRef().getPath();
Representation entryRep = app.getEntryRepresentation(feedBaseURI,feed,entry.getUUID());
// avoid a thread with the entry's output representation
StringWriter sw = new StringWriter();
entryRep.write(sw);
entryRep.release();
DocumentLoader loader = new SAXDocumentLoader();
Document doc = loader.load(new StringReader(sw.toString()));
// mark the entry as edited
Date date = media.getEntry().edited();
// change the entry's edited element
Element edited = doc.getDocumentElement().getFirstElementNamed(AtomResource.EDITED_NAME);
if (edited!=null) {
edited.clear();
edited.addCharacters(toXSDDate(date));
}
// store the change.
status = storage.storeEntry(feed.getPath(),feed.getUUID(),entry.getUUID(),doc);
if (status.isSuccess()) {
getResponse().setStatus(Status.SUCCESS_NO_CONTENT);
return null;
} else {
getResponse().setStatus(status);
return new StringRepresentation("Cannot update entry for media.");
}
} catch (Exception ex) {
getContext().getLogger().log(Level.SEVERE,"Cannot update media type: "+ex.getMessage(),ex);
getResponse().setStatus(Status.SERVER_ERROR_INTERNAL);
return new StringRepresentation("Cannot update media.");
}
}
public Representation delete()
{
try {
final String fpath = feed.getPath();
Entry entry = media.getEntry();
entry.delete(new MediaEntryListener() {
public void onDelete(EntryMedia resource) {
try {
storage.deleteMedia(fpath,feed.getUUID(),resource.getName());
} catch (IOException ex) {
getContext().getLogger().log(Level.SEVERE,"Cannot delete media: "+ex.getMessage(),ex);
}
}
});
if (storage.deleteEntry(fpath,feed.getUUID(),entry.getUUID())) {
getResponse().setStatus(Status.SUCCESS_NO_CONTENT);
return null;
} else {
getResponse().setStatus(Status.SERVER_ERROR_INTERNAL);
return new StringRepresentation("Cannot delete entry document.");
}
} catch (Exception ex) {
getContext().getLogger().log(Level.SEVERE,"Exception while deleting entry: "+ex.getMessage(),ex);
getResponse().setStatus(Status.SERVER_ERROR_INTERNAL);
return new StringRepresentation("Exception while deleting entry.");
}
}
public Representation head() {
try {
Representation dbRep = storage.getMediaHead(feed.getPath(),feed.getUUID(),media.getName());
dbRep.setMediaType(media.getMediaType());
String charset = dbRep.getMediaType().getParameters().getFirstValue("charset");
if (charset!=null) {
dbRep.setCharacterSet(CharacterSet.valueOf(charset));
}
getResponse().setStatus(Status.SUCCESS_NO_CONTENT);
return dbRep;
} catch (IOException ex) {
getContext().getLogger().log(Level.SEVERE,"Exception while getting media: "+ex.getMessage(),ex);
getResponse().setStatus(Status.SERVER_ERROR_INTERNAL);
return new StringRepresentation("Exception while getting entry.");
} catch (SQLException ex) {
getContext().getLogger().log(Level.SEVERE,"Exception while getting media: "+ex.getMessage(),ex);
getResponse().setStatus(Status.SERVER_ERROR_INTERNAL);
return new StringRepresentation("Exception while getting entry.");
}
}
/*
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());
propfind.setRequestDepth(0);
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 FeedResource.MediaDAVResource(getRequest().getResourceRef().toString(), media));
w.flush();
}
});
getResponse().getEntity().setCharacterSet(CharacterSet.UTF_8);
}
}
*/
@Move
public Representation move() {
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) {
getResponse().setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
return new StringRepresentation("Missing the 'Slug' header for the move target");
}
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);
getResponse().setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
return new StringRepresentation("Bad 'Slug' header value: "+slug);
}
try {
Representation entity = storage.getMedia(feed.getPath(),feed.getUUID(),media.getName());
entity.setMediaType(media.getMediaType());
if (feed.findEntryResource(slug)!=null) {
getResponse().setStatus(Status.CLIENT_ERROR_CONFLICT);
return new StringRepresentation("Resource already exists with name "+slug);
}
// TODO: move should use rename
Status status = storage.storeMedia(feed.getPath(),feed.getUUID(),slug,entity.getMediaType(),entity.getStream());
if (!status.isSuccess()) {
getResponse().setStatus(status);
return new StringRepresentation("Cannot store media to "+slug);
}
if (!storage.deleteMedia(feed.getPath(), feed.getUUID(), media.getName())) {
getLogger().severe("Cannot delete previous entry storage "+feed.getPath()+","+feed.getUUID()+","+feed.getName());
}
media.setName(slug);
media.touch();
// get the media resoruce's entry
Entry entry = media.getEntry();
// Get the entry's representation
String feedBaseURI = getRequest().getResourceRef().getParentRef().getPath();
Representation entryRep = app.getEntryRepresentation(feedBaseURI,feed,entry.getUUID());
// avoid a thread with the entry's output representation
StringWriter sw = new StringWriter();
entryRep.write(sw);
entryRep.release();
DocumentLoader loader = new SAXDocumentLoader();
Document doc = loader.load(new StringReader(sw.toString()));
// mark the entry as edited
Date date = media.getEntry().edited();
// change the entry's edited element
Element edited = doc.getDocumentElement().getFirstElementNamed(AtomResource.EDITED_NAME);
if (edited!=null) {
edited.clear();
edited.addCharacters(toXSDDate(date));
}
Element content = doc.getDocumentElement().getFirstElementNamed(AtomResource.CONTENT_NAME);
content.setAttributeValue("src",slug);
Iterator<Element> links = doc.getDocumentElement().getElementsByName(AtomResource.LINK_NAME);
while (links.hasNext()) {
Element link = links.next();
if ("edit-media".equals(link.getAttributeValue("rel"))) {
link.setAttributeValue("href", slug);
}
}
// store the change.
status = storage.storeEntry(feed.getPath(),feed.getUUID(),entry.getUUID(),doc);
if (status.isSuccess()) {
getResponse().setStatus(Status.SUCCESS_OK);
Form returnHeaders = new Form();
getResponse().getAttributes().put("org.restlet.http.headers",returnHeaders);
Reference ref = new Reference(getRequest().getResourceRef().getParentRef()+"_/"+entry.getUUID().toString());
getResponse().setLocationRef(ref);
Representation rep = app.getEntryRepresentation(getRequest().getResourceRef().getParentRef().toString(),entry);
rep.setCharacterSet(CharacterSet.UTF_8);
rep.setTag(new Tag(Long.toString(entry.getEdited().getTime()),false));
return rep;
} else {
getResponse().setStatus(status);
return new StringRepresentation("Cannot update entry for media.");
}
} catch (Exception ex) {
getContext().getLogger().log(Level.SEVERE,"Cannot move media.",ex);
getResponse().setStatus(Status.SERVER_ERROR_INTERNAL);
return new StringRepresentation("Cannot move media.");
}
}
@Copy
public Representation copy() {
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) {
getResponse().setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
return new StringRepresentation("Missing the 'Slug' header for the move target");
}
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);
getResponse().setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
return new StringRepresentation("Bad 'Slug' header value: "+slug);
}
try {
// Get author name for identity
User user = (User)getRequest().getAttributes().get(App.USER_ATTR);
Representation entity = storage.getMedia(feed.getPath(),feed.getUUID(),media.getName());
entity.setMediaType(media.getMediaType());
UUID id = UUID.randomUUID();
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(getRequest().getResourceRef().getParentRef()+"_/"+entry.getUUID().toString());
getResponse().setLocationRef(ref);
Representation rep = app.getEntryRepresentation(getRequest().getResourceRef().getParentRef().toString(),entry);
rep.setCharacterSet(CharacterSet.UTF_8);
rep.setTag(new Tag(Long.toString(entry.getEdited().getTime()),false));
return rep;
} catch (AppException ex) {
getResponse().setStatus(ex.getStatus());
if (ex.getStatus().getCode()==Status.SERVER_ERROR_INTERNAL.getCode()) {
getContext().getLogger().log(Level.SEVERE,ex.getMessage(),ex.getCause());
return new StringRepresentation("Internal error, see logs.");
} else {
return new StringRepresentation(ex.getMessage());
}
} catch (Exception ex) {
getContext().getLogger().log(Level.SEVERE,"Cannot copy media.",ex);
getResponse().setStatus(Status.SERVER_ERROR_INTERNAL);
return new StringRepresentation("Internal error, see logs.");
}
}
}