You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@roller.apache.org by sn...@apache.org on 2007/10/11 15:00:15 UTC
svn commit: r583812 [1/2] - in
/roller/branches/roller_4.1_dev/apps/weblogger/src/java/org/apache/roller/weblogger/webservices/atomprotocol:
CommentCollection.java EntryCollection.java MediaCollection.java
RollerAtomHandler.java
Author: snoopdave
Date: Thu Oct 11 06:00:14 2007
New Revision: 583812
URL: http://svn.apache.org/viewvc?rev=583812&view=rev
Log:
Refactoring AtomPub support into separate collections for entries, media and comments.
Added:
roller/branches/roller_4.1_dev/apps/weblogger/src/java/org/apache/roller/weblogger/webservices/atomprotocol/CommentCollection.java
roller/branches/roller_4.1_dev/apps/weblogger/src/java/org/apache/roller/weblogger/webservices/atomprotocol/EntryCollection.java
roller/branches/roller_4.1_dev/apps/weblogger/src/java/org/apache/roller/weblogger/webservices/atomprotocol/MediaCollection.java
Modified:
roller/branches/roller_4.1_dev/apps/weblogger/src/java/org/apache/roller/weblogger/webservices/atomprotocol/RollerAtomHandler.java
Added: roller/branches/roller_4.1_dev/apps/weblogger/src/java/org/apache/roller/weblogger/webservices/atomprotocol/CommentCollection.java
URL: http://svn.apache.org/viewvc/roller/branches/roller_4.1_dev/apps/weblogger/src/java/org/apache/roller/weblogger/webservices/atomprotocol/CommentCollection.java?rev=583812&view=auto
==============================================================================
--- roller/branches/roller_4.1_dev/apps/weblogger/src/java/org/apache/roller/weblogger/webservices/atomprotocol/CommentCollection.java (added)
+++ roller/branches/roller_4.1_dev/apps/weblogger/src/java/org/apache/roller/weblogger/webservices/atomprotocol/CommentCollection.java Thu Oct 11 06:00:14 2007
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Licensed 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.roller.weblogger.webservices.atomprotocol;
+
+import com.sun.syndication.feed.atom.Entry;
+import com.sun.syndication.feed.atom.Feed;
+import com.sun.syndication.propono.atom.server.AtomException;
+import com.sun.syndication.propono.atom.server.AtomRequest;
+
+
+/**
+ * Collection of weblog entry comments.
+ * @author davidm.johnson@sun.com
+ */
+public class CommentCollection {
+
+ public String postEntry(AtomRequest areq, Entry entry) throws AtomException {
+ return null;
+ }
+
+ public Entry getEntry(AtomRequest areq) throws AtomException {
+ return null;
+ }
+
+ public Feed getCollection(AtomRequest areg) throws AtomException {
+ return null;
+ }
+
+ public void putEntry(AtomRequest areq, Entry entry) throws AtomException {
+ }
+
+ public void deleteEntry(AtomRequest areq) throws AtomException {
+ }
+}
\ No newline at end of file
Added: roller/branches/roller_4.1_dev/apps/weblogger/src/java/org/apache/roller/weblogger/webservices/atomprotocol/EntryCollection.java
URL: http://svn.apache.org/viewvc/roller/branches/roller_4.1_dev/apps/weblogger/src/java/org/apache/roller/weblogger/webservices/atomprotocol/EntryCollection.java?rev=583812&view=auto
==============================================================================
--- roller/branches/roller_4.1_dev/apps/weblogger/src/java/org/apache/roller/weblogger/webservices/atomprotocol/EntryCollection.java (added)
+++ roller/branches/roller_4.1_dev/apps/weblogger/src/java/org/apache/roller/weblogger/webservices/atomprotocol/EntryCollection.java Thu Oct 11 06:00:14 2007
@@ -0,0 +1,522 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Licensed 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.roller.weblogger.webservices.atomprotocol;
+
+import com.sun.syndication.feed.atom.Category;
+import com.sun.syndication.feed.atom.Content;
+import com.sun.syndication.feed.atom.Entry;
+import com.sun.syndication.feed.atom.Feed;
+import com.sun.syndication.feed.atom.Link;
+import com.sun.syndication.feed.atom.Person;
+import com.sun.syndication.propono.atom.common.rome.AppModule;
+import com.sun.syndication.propono.atom.common.rome.AppModuleImpl;
+import com.sun.syndication.propono.atom.server.AtomException;
+import com.sun.syndication.propono.atom.server.AtomNotAuthorizedException;
+import com.sun.syndication.propono.atom.server.AtomNotFoundException;
+import com.sun.syndication.propono.atom.server.AtomRequest;
+import java.sql.Timestamp;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+import javax.activation.FileTypeMap;
+import javax.activation.MimetypesFileTypeMap;
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.roller.weblogger.WebloggerException;
+import org.apache.roller.weblogger.business.URLStrategy;
+import org.apache.roller.weblogger.business.WeblogEntryManager;
+import org.apache.roller.weblogger.business.Weblogger;
+import org.apache.roller.weblogger.business.WebloggerFactory;
+import org.apache.roller.weblogger.business.search.IndexManager;
+import org.apache.roller.weblogger.config.WebloggerRuntimeConfig;
+import org.apache.roller.weblogger.pojos.ThemeResource;
+import org.apache.roller.weblogger.pojos.User;
+import org.apache.roller.weblogger.pojos.Weblog;
+import org.apache.roller.weblogger.pojos.WeblogCategory;
+import org.apache.roller.weblogger.pojos.WeblogEntry;
+import org.apache.roller.weblogger.pojos.WeblogEntryTag;
+import org.apache.roller.weblogger.util.Utilities;
+import org.apache.roller.weblogger.util.cache.CacheManager;
+
+
+/**
+ * Collection of weblog entries.
+ * @author davidm.johnson@sun.com
+ */
+public class EntryCollection {
+ private Weblogger roller;
+ private User user;
+ private int maxEntries = 20;
+ private final String atomURL;
+
+ private static Log log =
+ LogFactory.getFactory().getInstance(EntryCollection.class);
+
+
+ public EntryCollection(User user, String atomURL) {
+ this.user = user;
+ this.atomURL = atomURL;
+ this.roller = WebloggerFactory.getWeblogger();
+ }
+
+
+ public String postEntry(AtomRequest areq, Entry entry) throws AtomException {
+ log.debug("Entering");
+ String[] pathInfo = StringUtils.split(areq.getPathInfo(),"/");
+ try {
+ // authenticated client posted a weblog entry
+ String handle = pathInfo[0];
+ Weblog website =
+ roller.getWeblogManager().getWeblogByHandle(handle);
+ if (website == null) {
+ throw new AtomNotFoundException("Cannot find weblog: " + handle);
+ }
+ if (!RollerAtomHandler.canEdit(user, website)) {
+ throw new AtomNotAuthorizedException("Not authorized to access website: " + handle);
+ }
+
+ RollerAtomHandler.oneSecondThrottle();
+
+ // Save it and commit it
+ WeblogEntryManager mgr = roller.getWeblogEntryManager();
+ WeblogEntry rollerEntry = new WeblogEntry();
+ rollerEntry.setWebsite(website);
+ rollerEntry.setCreatorUserName(this.user.getUserName());
+ copyToRollerEntry(entry, rollerEntry);
+ mgr.saveWeblogEntry(rollerEntry);
+ roller.flush();
+
+ CacheManager.invalidate(website);
+ if (rollerEntry.isPublished()) {
+ roller.getIndexManager().addEntryReIndexOperation(rollerEntry);
+ }
+
+ Entry newEntry = createAtomEntry(rollerEntry);
+ for (Iterator it = newEntry.getOtherLinks().iterator(); it.hasNext();) {
+ Link link = (Link)it.next();
+ if ("edit".equals(link.getRel())) {
+ log.debug("Exiting");
+ return link.getHrefResolved();
+ }
+ }
+ log.error("ERROR: no edit link found in saved media entry");
+ log.debug("Exiting");
+
+ } catch (WebloggerException re) {
+ throw new AtomException("Posting entry", re);
+ }
+ throw new AtomException("Posting entry");
+ }
+
+
+ public Entry getEntry(AtomRequest areq) throws AtomException {
+ try {
+ String entryid = Utilities.stringToStringArray(areq.getPathInfo(),"/")[2];
+ WeblogEntry entry = roller.getWeblogEntryManager().getWeblogEntry(entryid);
+ if (entry == null) {
+ throw new AtomNotFoundException("Cannot find specified entry/resource");
+ }
+ if (!RollerAtomHandler.canView(user, entry)) {
+ throw new AtomNotAuthorizedException("Not authorized to view entry");
+ } else {
+ return createAtomEntry(entry);
+ }
+ } catch (WebloggerException ex) {
+ throw new AtomException("ERROR fetching entry", ex);
+ }
+ }
+
+
+ public Feed getCollection(AtomRequest areq) throws AtomException {
+ log.debug("Entering");
+ String[] pathInfo = StringUtils.split(areq.getPathInfo(),"/");
+ try {
+ int start = 0;
+ int max = maxEntries;
+ if (pathInfo.length > 2) {
+ try {
+ String s = pathInfo[2].trim();
+ start = Integer.parseInt(s);
+ } catch (Throwable t) {
+ log.warn("Unparsable range: " + pathInfo[2]);
+ }
+ }
+ String handle = pathInfo[0];
+ String absUrl = WebloggerRuntimeConfig.getAbsoluteContextURL();
+ Weblog website = roller.getWeblogManager().getWeblogByHandle(handle);
+ if (website == null) {
+ throw new AtomNotFoundException("Cannot find specified weblog");
+ }
+ if (!RollerAtomHandler.canView(user, website)) {
+ throw new AtomNotAuthorizedException("Not authorized to access website: " + handle);
+ }
+ List entries = entries = roller.getWeblogEntryManager().getWeblogEntries(
+ website, // website
+ null, // user
+ null, // startDate
+ null, // endDate
+ null, // catName
+ null, // tags
+ null, // status
+ null, // text
+ "updateTime", // sortby
+ null,
+ null, // locale
+ start, // offset (for range paging)
+ max + 1); // maxEntries
+ Feed feed = new Feed();
+ feed.setId(atomURL
+ +"/"+website.getHandle() + "/entries/" + start);
+ feed.setTitle(website.getName());
+
+ Link link = new Link();
+ link.setHref(absUrl + "/" + website.getHandle());
+ link.setRel("alternate");
+ link.setType("text/html");
+ feed.setAlternateLinks(Collections.singletonList(link));
+
+ List atomEntries = new ArrayList();
+ int count = 0;
+ for (Iterator iter = entries.iterator(); iter.hasNext() && count < maxEntries; count++) {
+ WeblogEntry rollerEntry = (WeblogEntry)iter.next();
+ Entry entry = createAtomEntry(rollerEntry);
+ atomEntries.add(entry);
+ if (count == 0) {
+ // first entry is most recent
+ feed.setUpdated(entry.getUpdated());
+ }
+ }
+ List links = new ArrayList();
+ if (entries.size() > max) { // add next link
+ int nextOffset = start + max;
+ String url = atomURL+"/"
+ + website.getHandle() + "/entries/" + nextOffset;
+ Link nextLink = new Link();
+ nextLink.setRel("next");
+ nextLink.setHref(url);
+ links.add(nextLink);
+ }
+ if (start > 0) { // add previous link
+ int prevOffset = start > max ? start - max : 0;
+ String url = atomURL+"/"
+ +website.getHandle() + "/entries/" + prevOffset;
+ Link prevLink = new Link();
+ prevLink.setRel("previous");
+ prevLink.setHref(url);
+ links.add(prevLink);
+ }
+ if (links.size() > 0) feed.setOtherLinks(links);
+ // Use collection URI as id
+ feed.setEntries(atomEntries);
+
+ log.debug("Exiting");
+ return feed;
+
+ } catch (WebloggerException re) {
+ throw new AtomException("Getting entry collection");
+ }
+ }
+
+
+ public void putEntry(AtomRequest areq, Entry entry) throws AtomException {
+ log.debug("Entering");
+ String[] pathInfo = StringUtils.split(areq.getPathInfo(),"/");
+ try {
+ if (pathInfo.length == 3) // URI is /blogname/entries/entryid
+ {
+ WeblogEntry rollerEntry =
+ roller.getWeblogEntryManager().getWeblogEntry(pathInfo[2]);
+ if (rollerEntry == null) {
+ throw new AtomNotFoundException(
+ "Cannot find specified entry/resource");
+ }
+ if (RollerAtomHandler.canEdit(user, rollerEntry)) {
+
+ RollerAtomHandler.oneSecondThrottle();
+
+ WeblogEntryManager mgr = roller.getWeblogEntryManager();
+ copyToRollerEntry(entry, rollerEntry);
+ rollerEntry.setUpdateTime(new Timestamp(new Date().getTime()));
+ mgr.saveWeblogEntry(rollerEntry);
+ roller.flush();
+
+ CacheManager.invalidate(rollerEntry.getWebsite());
+ if (rollerEntry.isPublished()) {
+ roller.getIndexManager().addEntryReIndexOperation(rollerEntry);
+ }
+ log.debug("Exiting");
+ return;
+ }
+ throw new AtomNotAuthorizedException("ERROR not authorized to update entry");
+ }
+ throw new AtomNotFoundException("Cannot find specified entry/resource");
+
+ } catch (WebloggerException re) {
+ throw new AtomException("Updating entry");
+ }
+ }
+
+
+ public void deleteEntry(AtomRequest areq) throws AtomException {
+ try {
+ String[] pathInfo = StringUtils.split(areq.getPathInfo(), "/");
+ WeblogEntry rollerEntry = roller.getWeblogEntryManager().getWeblogEntry(pathInfo[2]);
+ if (rollerEntry == null) {
+ throw new AtomNotFoundException("cannot find specified entry/resource");
+ }
+ if (RollerAtomHandler.canEdit(user, rollerEntry)) {
+ WeblogEntryManager mgr = roller.getWeblogEntryManager();
+ CacheManager.invalidate(rollerEntry.getWebsite());
+ reindexEntry(rollerEntry);
+ mgr.removeWeblogEntry(rollerEntry);
+ log.debug("Deleted entry:" + rollerEntry.getAnchor());
+ roller.flush();
+ return;
+ }
+ } catch (WebloggerException ex) {
+ throw new AtomException("ERROR deleting entry",ex);
+ }
+ }
+
+
+ /**
+ * Create a Rome Atom entry based on a Weblogger entry.
+ * Content is escaped.
+ * Link is stored as rel=alternate link.
+ */
+ private Entry createAtomEntry(WeblogEntry entry) {
+ Entry atomEntry = new Entry();
+
+ String absUrl = WebloggerRuntimeConfig.getAbsoluteContextURL();
+ atomEntry.setId( absUrl + entry.getPermaLink());
+ atomEntry.setTitle( entry.getTitle());
+ atomEntry.setPublished( entry.getPubTime());
+ atomEntry.setUpdated( entry.getUpdateTime());
+
+ Content content = new Content();
+ content.setType(Content.HTML);
+ content.setValue(entry.getText());
+ List contents = new ArrayList();
+ contents.add(content);
+
+ atomEntry.setContents(contents);
+
+ if (StringUtils.isNotEmpty(entry.getSummary())) {
+ Content summary = new Content();
+ summary.setType(Content.HTML);
+ summary.setValue(entry.getSummary());
+ atomEntry.setSummary(summary);
+ }
+
+ User creator = entry.getCreator();
+ Person author = new Person();
+ author.setName( creator.getUserName());
+ author.setEmail( creator.getEmailAddress());
+ atomEntry.setAuthors( Collections.singletonList(author));
+
+ // Add Atom category for Weblogger category, using category scheme
+ List categories = new ArrayList();
+ Category atomCat = new Category();
+ atomCat.setScheme(RollerAtomService.getWeblogCategoryScheme(entry.getWebsite()));
+ atomCat.setTerm(entry.getCategory().getPath().substring(1));
+ categories.add(atomCat);
+
+ // Add Atom categories for each Weblogger tag with null scheme
+ for (Iterator tagit = entry.getTags().iterator(); tagit.hasNext();) {
+ WeblogEntryTag tag = (WeblogEntryTag) tagit.next();
+ Category newcat = new Category();
+ newcat.setTerm(tag.getName());
+ categories.add(newcat);
+ }
+ atomEntry.setCategories(categories);
+
+ Link altlink = new Link();
+ altlink.setRel("alternate");
+ altlink.setHref(absUrl + entry.getPermaLink());
+ List altlinks = new ArrayList();
+ altlinks.add(altlink);
+ atomEntry.setAlternateLinks(altlinks);
+
+ Link editlink = new Link();
+ editlink.setRel("edit");
+ editlink.setHref(
+ atomURL
+ +"/"+entry.getWebsite().getHandle() + "/entry/" + entry.getId());
+ List otherlinks = new ArrayList();
+ otherlinks.add(editlink);
+ atomEntry.setOtherLinks(otherlinks);
+
+ List modules = new ArrayList();
+ AppModule app = new AppModuleImpl();
+ app.setDraft(!WeblogEntry.PUBLISHED.equals(entry.getStatus()));
+ app.setEdited(entry.getUpdateTime());
+ modules.add(app);
+ atomEntry.setModules(modules);
+
+ return atomEntry;
+ }
+
+ private Entry createAtomResourceEntry(Weblog website, ThemeResource file) {
+ String absUrl = WebloggerRuntimeConfig.getAbsoluteContextURL();
+ String filePath =
+ file.getPath().startsWith("/") ? file.getPath().substring(1) : file.getPath();
+ String editURI =
+ atomURL+"/"+website.getHandle()
+ + "/resource/" + filePath + ".media-link";
+ String editMediaURI =
+ atomURL+"/"+ website.getHandle()
+ + "/resource/" + filePath;
+ URLStrategy urlStrategy = WebloggerFactory.getWeblogger().getUrlStrategy();
+ String viewURI = urlStrategy.getWeblogResourceURL(website, filePath, true);
+
+ FileTypeMap map = FileTypeMap.getDefaultFileTypeMap();
+ // TODO: figure out why PNG is missing from Java MIME types
+ if (map instanceof MimetypesFileTypeMap) {
+ try {
+ ((MimetypesFileTypeMap)map).addMimeTypes("image/png png PNG");
+ } catch (Exception ignored) {}
+ }
+ String contentType = map.getContentType(file.getName());
+
+ Entry entry = new Entry();
+ entry.setId(editMediaURI);
+ entry.setTitle(file.getName());
+ entry.setUpdated(new Date(file.getLastModified()));
+
+ List otherlinks = new ArrayList();
+ entry.setOtherLinks(otherlinks);
+ Link editlink = new Link();
+ editlink.setRel("edit");
+ editlink.setHref(editURI);
+ otherlinks.add(editlink);
+ Link editMedialink = new Link();
+ editMedialink.setRel("edit-media");
+ editMedialink.setHref(editMediaURI);
+ otherlinks.add(editMedialink);
+
+ Content content = new Content();
+ content.setSrc(viewURI);
+ content.setType(contentType);
+ List contents = new ArrayList();
+ contents.add(content);
+ entry.setContents(contents);
+
+ List modules = new ArrayList();
+ AppModule app = new AppModuleImpl();
+ app.setDraft(false);
+ app.setEdited(entry.getUpdated());
+ modules.add(app);
+ entry.setModules(modules);
+
+ return entry;
+ }
+
+
+ /**
+ * Copy fields from ROME entry to Weblogger entry.
+ */
+ private void copyToRollerEntry(Entry entry, WeblogEntry rollerEntry) throws WebloggerException {
+
+ Timestamp current = new Timestamp(System.currentTimeMillis());
+ Timestamp pubTime = current;
+ Timestamp updateTime = current;
+ if (entry.getPublished() != null) {
+ pubTime = new Timestamp( entry.getPublished().getTime() );
+ }
+ if (entry.getUpdated() != null) {
+ updateTime = new Timestamp( entry.getUpdated().getTime() );
+ }
+ rollerEntry.setTitle(entry.getTitle());
+ if (entry.getContents() != null && entry.getContents().size() > 0) {
+ Content content = (Content)entry.getContents().get(0);
+ rollerEntry.setText(content.getValue());
+ }
+ if (entry.getSummary() != null) {
+ rollerEntry.setSummary(entry.getSummary().getValue());
+ }
+ rollerEntry.setPubTime(pubTime);
+ rollerEntry.setUpdateTime(updateTime);
+
+ AppModule control =
+ (AppModule)entry.getModule(AppModule.URI);
+ if (control!=null && control.getDraft()) {
+ rollerEntry.setStatus(WeblogEntry.DRAFT);
+ } else {
+ rollerEntry.setStatus(WeblogEntry.PUBLISHED);
+ }
+
+ // Process incoming categories:
+ // Atom categories with weblog-level scheme are Weblogger categories.
+ // Atom supports multiple cats, but Weblogger supports one/entry
+ // so here we take accept the first category that exists.
+ List categories = entry.getCategories();
+ if (categories != null && categories.size() > 0) {
+ for (int i=0; i<categories.size(); i++) {
+ Category cat = (Category)categories.get(i);
+
+ if (cat.getScheme() != null && cat.getScheme().equals(
+ RollerAtomService.getWeblogCategoryScheme(rollerEntry.getWebsite()))) {
+ String catString = cat.getTerm();
+ if (catString != null) {
+ WeblogCategory rollerCat =
+ roller.getWeblogEntryManager().getWeblogCategoryByPath(
+ rollerEntry.getWebsite(), catString);
+ if (rollerCat != null) {
+ // Found a valid category, so break out
+ rollerEntry.setCategory(rollerCat);
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (rollerEntry.getCategory() == null) {
+ // Didn't find a category? Fall back to the default Blogger API category.
+ rollerEntry.setCategory(rollerEntry.getWebsite().getBloggerCategory());
+ }
+
+ // Now process incoming categories that are tags:
+ // Atom categories with no scheme are considered tags.
+ String tags = "";
+ if (categories != null && categories.size() > 0) {
+ for (int i=0; i<categories.size(); i++) {
+ Category cat = (Category)categories.get(i);
+ if (cat.getScheme() == null) {
+ tags = tags + " " + cat.getTerm();
+ }
+ }
+ }
+ rollerEntry.setTagsAsString(tags);
+ }
+
+
+ private void reindexEntry(WeblogEntry entry) throws WebloggerException {
+ IndexManager manager = roller.getIndexManager();
+
+ // TODO: figure out what's up here and at WeblogEntryFormAction line 696
+ //manager.removeEntryIndexOperation(entry);
+
+ // if published, index the entry
+ if (entry.isPublished()) {
+ manager.addEntryReIndexOperation(entry);
+ }
+ }
+}
\ No newline at end of file
Added: roller/branches/roller_4.1_dev/apps/weblogger/src/java/org/apache/roller/weblogger/webservices/atomprotocol/MediaCollection.java
URL: http://svn.apache.org/viewvc/roller/branches/roller_4.1_dev/apps/weblogger/src/java/org/apache/roller/weblogger/webservices/atomprotocol/MediaCollection.java?rev=583812&view=auto
==============================================================================
--- roller/branches/roller_4.1_dev/apps/weblogger/src/java/org/apache/roller/weblogger/webservices/atomprotocol/MediaCollection.java (added)
+++ roller/branches/roller_4.1_dev/apps/weblogger/src/java/org/apache/roller/weblogger/webservices/atomprotocol/MediaCollection.java Thu Oct 11 06:00:14 2007
@@ -0,0 +1,542 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Licensed 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.roller.weblogger.webservices.atomprotocol;
+
+import com.sun.syndication.feed.atom.Content;
+import com.sun.syndication.feed.atom.Entry;
+import com.sun.syndication.feed.atom.Feed;
+import com.sun.syndication.feed.atom.Link;
+import com.sun.syndication.propono.atom.common.rome.AppModule;
+import com.sun.syndication.propono.atom.common.rome.AppModuleImpl;
+import com.sun.syndication.propono.atom.server.AtomException;
+import com.sun.syndication.propono.atom.server.AtomMediaResource;
+import com.sun.syndication.propono.atom.server.AtomNotAuthorizedException;
+import com.sun.syndication.propono.atom.server.AtomNotFoundException;
+import com.sun.syndication.propono.atom.server.AtomRequest;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+import java.util.SortedSet;
+import java.util.StringTokenizer;
+import java.util.TreeSet;
+import java.util.UUID;
+import javax.activation.FileTypeMap;
+import javax.activation.MimetypesFileTypeMap;
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.roller.weblogger.WebloggerException;
+import org.apache.roller.weblogger.business.FileIOException;
+import org.apache.roller.weblogger.business.FileManager;
+import org.apache.roller.weblogger.business.URLStrategy;
+import org.apache.roller.weblogger.business.WeblogManager;
+import org.apache.roller.weblogger.business.Weblogger;
+import org.apache.roller.weblogger.business.WebloggerFactory;
+import org.apache.roller.weblogger.config.WebloggerRuntimeConfig;
+import org.apache.roller.weblogger.pojos.ThemeResource;
+import org.apache.roller.weblogger.pojos.User;
+import org.apache.roller.weblogger.pojos.Weblog;
+import org.apache.roller.weblogger.util.Utilities;
+
+
+/**
+ * Collection of media resources.
+ * @author davidm.johnson@sun.com
+ */
+public class MediaCollection {
+ private Weblogger roller;
+ private User user;
+ private int maxEntries = 20;
+ private final String atomURL;
+
+ private static Log log =
+ LogFactory.getFactory().getInstance(EntryCollection.class);
+
+
+ public MediaCollection(User user, String atomURL) {
+ this.user = user;
+ this.atomURL = atomURL;
+ this.roller = WebloggerFactory.getWeblogger();
+ }
+
+
+ public String postMedia(AtomRequest areq, Entry entry) throws AtomException {
+ log.debug("Entering");
+ String[] pathInfo = StringUtils.split(areq.getPathInfo(),"/");
+
+ try {
+ // get incoming slug from HTTP header
+ String slug = areq.getHeader("Slug");
+
+ Content content = (Content)entry.getContents().get(0);
+ String contentType = content.getType();
+ InputStream is = areq.getInputStream();
+ String title = entry.getTitle() != null ? entry.getTitle() : slug;
+
+ // authenticated client posted a weblog entry
+ File tempFile = null;
+ String handle = pathInfo[0];
+ FileManager fmgr = roller.getFileManager();
+ Weblog website = WebloggerFactory.getWeblogger().getWeblogManager().getWeblogByHandle(handle);
+ if (!RollerAtomHandler.canEdit(user, website)) {
+ throw new AtomNotAuthorizedException("Not authorized to edit weblog: " + handle);
+ }
+ if (pathInfo.length > 1) {
+ // Save to temp file
+ String fileName = createFileName(website,
+ (slug != null) ? slug : Utilities.replaceNonAlphanumeric(title,' '), contentType);
+ try {
+ tempFile = File.createTempFile(fileName, "tmp");
+ FileOutputStream fos = new FileOutputStream(tempFile);
+ Utilities.copyInputToOutput(is, fos);
+ fos.close();
+
+ // Parse pathinfo to determine file path
+ String path = filePathFromPathInfo(pathInfo);
+
+ if (path.length() > 0) path = path + File.separator;
+ FileInputStream fis = new FileInputStream(tempFile);
+ fmgr.saveFile(website, path + fileName, contentType, tempFile.length(), fis);
+ fis.close();
+
+ ThemeResource resource = fmgr.getFile(website, path + fileName);
+
+
+ Entry mediaEntry = createAtomResourceEntry(website, resource);
+ for (Iterator it = mediaEntry.getOtherLinks().iterator(); it.hasNext();) {
+ Link link = (Link)it.next();
+ if ("edit".equals(link.getRel())) {
+ log.debug("Exiting");
+ return link.getHrefResolved();
+ }
+ }
+ log.error("ERROR: no edit link found in saved media entry");
+
+ } catch (FileIOException fie) {
+ throw new AtomException(
+ "File upload disabled, over-quota or other error", fie);
+ } catch (Exception e) {
+ throw new AtomException(
+ "Unexpected error during file upload", e);
+ } finally {
+ if (tempFile != null) tempFile.delete();
+ }
+ }
+ throw new AtomException("Error saving media entry");
+
+ } catch (WebloggerException re) {
+ throw new AtomException("Posting media", re);
+ } catch (IOException ioe) {
+ throw new AtomException("Posting media", ioe);
+ }
+ }
+
+
+ public Entry getEntry(AtomRequest areq) throws AtomException {
+ try {
+ String[] pathInfo = Utilities.stringToStringArray(areq.getPathInfo(), "/");
+
+ String filePath = filePathFromPathInfo(pathInfo);
+ filePath = filePath.substring(0, filePath.length() - ".media-link".length());
+ String handle = pathInfo[0];
+ Weblog website = roller.getWeblogManager().getWeblogByHandle(handle);
+ ThemeResource resource = roller.getFileManager().getFile(website, filePath);
+
+ log.debug("Exiting");
+ if (resource != null) {
+ return createAtomResourceEntry(website, resource);
+ }
+
+ } catch (WebloggerException ex) {
+ throw new AtomException("ERROR fetching entry",ex);
+ }
+ throw new AtomNotFoundException("ERROR resource not found");
+ }
+
+
+ public AtomMediaResource getMediaResource(AtomRequest areq) throws AtomException {
+ log.debug("Entering");
+ String[] pathInfo = StringUtils.split(areq.getPathInfo(),"/");
+ try {
+ // authenticated client posted a weblog entry
+ File tempFile = null;
+ String handle = pathInfo[0];
+ FileManager fmgr = roller.getFileManager();
+ Weblog website = WebloggerFactory.getWeblogger().getWeblogManager().getWeblogByHandle(handle);
+ if (!RollerAtomHandler.canEdit(user, website)) {
+ throw new AtomNotAuthorizedException("Not authorized to edit weblog: " + handle);
+ }
+ if (pathInfo.length > 1) {
+ try {
+ // Parse pathinfo to determine file path
+ String filePath = filePathFromPathInfo(pathInfo);
+ ThemeResource resource = fmgr.getFile(website, filePath);
+ return new AtomMediaResource(
+ resource.getName(),
+ resource.getLength(),
+ new Date(resource.getLastModified()),
+ resource.getInputStream());
+ } catch (Exception e) {
+ throw new AtomException(
+ "Unexpected error during file upload", e);
+ }
+ }
+ throw new AtomException("Incorrect path information");
+
+ } catch (WebloggerException re) {
+ throw new AtomException("Posting media");
+ }
+ }
+
+
+ public Feed getCollection(AtomRequest areq) throws AtomException {
+ log.debug("Entering");
+ String[] rawPathInfo = StringUtils.split(areq.getPathInfo(),"/");
+ try {
+ int start = 0;
+ int max = maxEntries;
+ String[] pathInfo = rawPathInfo;
+ if (rawPathInfo.length > 2) {
+ try {
+ start = Integer.parseInt(rawPathInfo[rawPathInfo.length - 1]);
+ pathInfo = new String[rawPathInfo.length - 1];
+ for (int i=0; i<rawPathInfo.length - 1; i++) {
+ pathInfo[i] = rawPathInfo[i];
+ }
+ } catch (Exception ingored) {}
+ }
+ String path = filePathFromPathInfo(pathInfo);
+ if (!path.equals("")) path = path + File.separator;
+
+ String handle = pathInfo[0];
+ String absUrl = WebloggerRuntimeConfig.getAbsoluteContextURL();
+ Weblog website = roller.getWeblogManager().getWeblogByHandle(handle);
+ if (website == null) {
+ throw new AtomNotFoundException("Cannot find weblog: " + handle);
+ }
+ if (!RollerAtomHandler.canView(user, website)) {
+ throw new AtomNotAuthorizedException("Not authorized to access website");
+ }
+
+ Feed feed = new Feed();
+ feed.setId(atomURL
+ +"/"+website.getHandle() + "/resources/" + path + start);
+ feed.setTitle(website.getName());
+
+ Link link = new Link();
+ link.setHref(absUrl + "/" + website.getHandle());
+ link.setRel("alternate");
+ link.setType("text/html");
+ feed.setAlternateLinks(Collections.singletonList(link));
+
+ FileManager fmgr = roller.getFileManager();
+ ThemeResource[] files = fmgr.getFiles(website, path);
+
+ SortedSet sortedSet = new TreeSet(new Comparator() {
+ public int compare(Object o1, Object o2) {
+ ThemeResource f1 = (ThemeResource)o1;
+ ThemeResource f2 = (ThemeResource)o2;
+ if (f1.getLastModified() < f2.getLastModified()) return 1;
+ else if (f1.getLastModified() == f2.getLastModified()) return 0;
+ else return -1;
+ }
+ });
+
+ if (files != null && start < files.length) {
+ for (int j=0; j<files.length; j++) {
+ sortedSet.add(files[j]);
+ }
+ int count = 0;
+ ThemeResource[] sortedResources =
+ (ThemeResource[])sortedSet.toArray(new ThemeResource[sortedSet.size()]);
+ List atomEntries = new ArrayList();
+ for (int i=start; i<(start + max) && i<(sortedResources.length); i++) {
+ Entry entry = createAtomResourceEntry(website, sortedResources[i]);
+ atomEntries.add(entry);
+ if (count == 0) {
+ // first entry is most recent
+ feed.setUpdated(entry.getUpdated());
+ }
+ count++;
+ }
+
+ List otherLinks = new ArrayList();
+ if (start + count < files.length) { // add next link
+ int nextOffset = start + max;
+ String url = atomURL
+ +"/"+ website.getHandle() + "/resources/" + path + nextOffset;
+ Link nextLink = new Link();
+ nextLink.setRel("next");
+ nextLink.setHref(url);
+ otherLinks.add(nextLink);
+ }
+ if (start > 0) { // add previous link
+ int prevOffset = start > max ? start - max : 0;
+ String url = atomURL
+ +"/"+website.getHandle() + "/resources/" + path + prevOffset;
+ Link prevLink = new Link();
+ prevLink.setRel("previous");
+ prevLink.setHref(url);
+ otherLinks.add(prevLink);
+ }
+ feed.setOtherLinks(otherLinks);
+ feed.setEntries(atomEntries);
+ }
+
+ log.debug("Existing");
+ return feed;
+
+ } catch (WebloggerException re) {
+ throw new AtomException("Getting resource collection");
+ }
+ }
+
+
+ public void putMedia(AtomRequest areq) throws AtomException {
+ String[] pathInfo = StringUtils.split(areq.getPathInfo(),"/");
+ String contentType = areq.getContentType();
+ try {
+ InputStream is = areq.getInputStream();
+
+ // authenticated client posted a weblog entry
+ File tempFile = null;
+ String handle = pathInfo[0];
+ FileManager fmgr = roller.getFileManager();
+ WeblogManager wmgr = roller.getWeblogManager();
+ Weblog website = wmgr.getWeblogByHandle(handle);
+ if (!RollerAtomHandler.canEdit(user, website)) {
+ throw new AtomNotAuthorizedException("Not authorized to edit weblog: " + handle);
+ }
+ if (pathInfo.length > 1) {
+ // Save to temp file
+ try {
+ tempFile = File.createTempFile(UUID.randomUUID().toString(), "tmp");
+ FileOutputStream fos = new FileOutputStream(tempFile);
+ Utilities.copyInputToOutput(is, fos);
+ fos.close();
+
+ // Parse pathinfo to determine file path
+ String path = filePathFromPathInfo(pathInfo);
+
+ // Attempt to load file, to ensure it exists
+ ThemeResource resource = fmgr.getFile(website, path);
+
+ FileInputStream fis = new FileInputStream(tempFile);
+ fmgr.saveFile(website, path, contentType, tempFile.length(), fis);
+ fis.close();
+
+ log.debug("Exiting");
+
+ } catch (FileIOException fie) {
+ throw new AtomException(
+ "File upload disabled, over-quota or other error", fie);
+ } catch (Exception e) {
+ throw new AtomException(
+ "Unexpected error during file upload", e);
+ } finally {
+ if (tempFile != null) tempFile.delete();
+ }
+ }
+ throw new AtomException("Incorrect path information");
+
+ } catch (WebloggerException re) {
+ throw new AtomException("Posting media");
+ } catch (IOException ioe) {
+ throw new AtomException("Posting media", ioe);
+ }
+ }
+
+
+ public void deleteEntry(AtomRequest areq) throws AtomException {
+ try {
+ String[] pathInfo = StringUtils.split(areq.getPathInfo(), "/");
+ String handle = pathInfo[0];
+ Weblog website = roller.getWeblogManager().getWeblogByHandle(handle);
+ if (website == null) {
+ throw new AtomNotFoundException("cannot find specified weblog");
+ }
+ if (RollerAtomHandler.canEdit(user, website) && pathInfo.length > 1) {
+ try {
+ String path = filePathFromPathInfo(pathInfo);
+ String fileName = path.substring(0, path.length() - ".media-link".length());
+ FileManager fmgr = roller.getFileManager();
+ fmgr.deleteFile(website, fileName);
+ log.debug("Deleted resource: " + fileName);
+ } catch (Exception e) {
+ String msg = "ERROR in atom.deleteResource";
+ log.error(msg, e);
+ throw new AtomException(msg);
+ }
+ return;
+ }
+ } catch (WebloggerException ex) {
+ throw new AtomException("ERROR deleting entry",ex);
+ }
+ }
+
+
+ private String filePathFromPathInfo(String[] pathInfo) {
+ String path = null;
+ if (pathInfo.length > 2) {
+ for (int i = 2; i < pathInfo.length; i++) {
+ if (path != null && path.length() > 0)
+ path = path + File.separator + pathInfo[i];
+ else
+ path = pathInfo[i];
+ }
+ } if (pathInfo.length == 2) {
+ path = "";
+ }
+ return path;
+ }
+
+ private Entry createAtomResourceEntry(Weblog website, ThemeResource file) {
+ String absUrl = WebloggerRuntimeConfig.getAbsoluteContextURL();
+ String filePath =
+ file.getPath().startsWith("/") ? file.getPath().substring(1) : file.getPath();
+ String editURI =
+ atomURL+"/"+website.getHandle()
+ + "/resource/" + filePath + ".media-link";
+ String editMediaURI =
+ atomURL+"/"+ website.getHandle()
+ + "/resource/" + filePath;
+ URLStrategy urlStrategy = WebloggerFactory.getWeblogger().getUrlStrategy();
+ String viewURI = urlStrategy.getWeblogResourceURL(website, filePath, true);
+
+ FileTypeMap map = FileTypeMap.getDefaultFileTypeMap();
+ // TODO: figure out why PNG is missing from Java MIME types
+ if (map instanceof MimetypesFileTypeMap) {
+ try {
+ ((MimetypesFileTypeMap)map).addMimeTypes("image/png png PNG");
+ } catch (Exception ignored) {}
+ }
+ String contentType = map.getContentType(file.getName());
+
+ Entry entry = new Entry();
+ entry.setId(editMediaURI);
+ entry.setTitle(file.getName());
+ entry.setUpdated(new Date(file.getLastModified()));
+
+ List otherlinks = new ArrayList();
+ entry.setOtherLinks(otherlinks);
+ Link editlink = new Link();
+ editlink.setRel("edit");
+ editlink.setHref(editURI);
+ otherlinks.add(editlink);
+ Link editMedialink = new Link();
+ editMedialink.setRel("edit-media");
+ editMedialink.setHref(editMediaURI);
+ otherlinks.add(editMedialink);
+
+ Content content = new Content();
+ content.setSrc(viewURI);
+ content.setType(contentType);
+ List contents = new ArrayList();
+ contents.add(content);
+ entry.setContents(contents);
+
+ List modules = new ArrayList();
+ AppModule app = new AppModuleImpl();
+ app.setDraft(false);
+ app.setEdited(entry.getUpdated());
+ modules.add(app);
+ entry.setModules(modules);
+
+ return entry;
+ }
+
+
+ /**
+ * Creates a file name for a file based on a weblog, title string and a
+ * content-type.
+ *
+ * @param weblog Weblog for which file name is being created
+ * @param title Title to be used as basis for file name (or null)
+ * @param contentType Content type of file (must not be null)
+ *
+ * If a title is specified, the method will apply the same create-anchor
+ * logic we use for weblog entries to create a file name based on the title.
+ *
+ * If title is null, the base file name will be the weblog handle plus a
+ * YYYYMMDDHHSS timestamp.
+ *
+ * The extension will be formed by using the part of content type that
+ * comes after he slash.
+ *
+ * For example:
+ * weblog.handle = "daveblog"
+ * title = "Port Antonio"
+ * content-type = "image/jpg"
+ * Would result in port_antonio.jpg
+ *
+ * Another example:
+ * weblog.handle = "daveblog"
+ * title = null
+ * content-type = "image/jpg"
+ * Might result in daveblog-200608201034.jpg
+ */
+ private String createFileName(Weblog weblog, String slug, String contentType) {
+
+ if (weblog == null) throw new IllegalArgumentException("weblog cannot be null");
+ if (contentType == null) throw new IllegalArgumentException("contentType cannot be null");
+
+ String fileName = null;
+
+ // Determine the extension based on the contentType. This is a hack.
+ // The info we need to map from contentType to file extension is in
+ // JRE/lib/content-type.properties, but Java Activation doesn't provide
+ // a way to do a reverse mapping or to get at the data.
+ String[] typeTokens = contentType.split("/");
+ String ext = typeTokens[1];
+
+ if (slug != null && !slug.trim().equals("")) {
+ // We've got a title, so use it to build file name
+ StringTokenizer toker = new StringTokenizer(slug);
+ String tmp = null;
+ int count = 0;
+ while (toker.hasMoreTokens() && count < 5) {
+ String s = toker.nextToken();
+ s = s.toLowerCase();
+ tmp = (tmp == null) ? s : tmp + "_" + s;
+ count++;
+ }
+ if (!tmp.endsWith("." + ext)) {
+ fileName = tmp + "." + ext;
+ } else {
+ fileName = tmp;
+ }
+ } else {
+ // No title or text, so instead we'll use the item's date
+ // in YYYYMMDD format to form the file name
+ SimpleDateFormat sdf = new SimpleDateFormat();
+ sdf.applyPattern("yyyyMMddHHSS");
+ fileName = weblog.getHandle()+"-"+sdf.format(new Date())+"."+ext;
+ }
+
+ return fileName;
+ }
+}