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 2006/04/20 18:22:56 UTC
svn commit: r395633 [1/2] - in /incubator/roller/trunk: custom/
sandbox/atomprotocol/ src/org/roller/presentation/webservices/
src/org/roller/presentation/webservices/atomprotocol/
src/org/roller/presentation/webservices/xmlrpc/ src/org/roller/presenta...
Author: snoopdave
Date: Thu Apr 20 09:22:49 2006
New Revision: 395633
URL: http://svn.apache.org/viewcvs?rev=395633&view=rev
Log:
Moving Atom Pub. Protocol implementation from sandbox to src, disabled by default with warning in roller.properties
Added:
incubator/roller/trunk/src/org/roller/presentation/webservices/
incubator/roller/trunk/src/org/roller/presentation/webservices/atomprotocol/
incubator/roller/trunk/src/org/roller/presentation/webservices/atomprotocol/AtomHandler.java
incubator/roller/trunk/src/org/roller/presentation/webservices/atomprotocol/AtomService.java
incubator/roller/trunk/src/org/roller/presentation/webservices/atomprotocol/AtomServlet.java
incubator/roller/trunk/src/org/roller/presentation/webservices/atomprotocol/PubControlModule.java
incubator/roller/trunk/src/org/roller/presentation/webservices/atomprotocol/PubControlModuleGenerator.java
incubator/roller/trunk/src/org/roller/presentation/webservices/atomprotocol/PubControlModuleImpl.java
incubator/roller/trunk/src/org/roller/presentation/webservices/atomprotocol/PubControlModuleParser.java
incubator/roller/trunk/src/org/roller/presentation/webservices/atomprotocol/RollerAtomHandler.java
incubator/roller/trunk/src/org/roller/presentation/webservices/atomprotocol/WSSEUtilities.java
incubator/roller/trunk/src/org/roller/presentation/webservices/atomprotocol/package.html
incubator/roller/trunk/src/org/roller/presentation/webservices/xmlrpc/
incubator/roller/trunk/src/org/roller/presentation/webservices/xmlrpc/BaseAPIHandler.java
incubator/roller/trunk/src/org/roller/presentation/webservices/xmlrpc/BloggerAPIHandler.java
incubator/roller/trunk/src/org/roller/presentation/webservices/xmlrpc/MetaWeblogAPIHandler.java
incubator/roller/trunk/src/org/roller/presentation/webservices/xmlrpc/RollerXMLRPCServlet.java
incubator/roller/trunk/src/org/roller/presentation/webservices/xmlrpc/package.html
Removed:
incubator/roller/trunk/sandbox/atomprotocol/
incubator/roller/trunk/src/org/roller/presentation/xmlrpc/
Modified:
incubator/roller/trunk/custom/custom-dbscripts.xmlf
incubator/roller/trunk/custom/custom-gen-beans.xmlf
incubator/roller/trunk/custom/custom-gen-web.xmlf
incubator/roller/trunk/custom/custom-jars.xmlf
incubator/roller/trunk/custom/custom-post-dbtest.xmlf
incubator/roller/trunk/custom/custom-pre-dbtest.xmlf
incubator/roller/trunk/custom/custom-src-beans.xmlf
incubator/roller/trunk/custom/custom-src-web.xmlf
incubator/roller/trunk/custom/custom-web.xmlf
incubator/roller/trunk/tests/org/roller/business/WeblogManagerTest.java
incubator/roller/trunk/web/WEB-INF/classes/roller.properties
incubator/roller/trunk/web/WEB-INF/classes/rome.properties
Modified: incubator/roller/trunk/custom/custom-dbscripts.xmlf
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/custom/custom-dbscripts.xmlf?rev=395633&r1=395632&r2=395633&view=diff
==============================================================================
--- incubator/roller/trunk/custom/custom-dbscripts.xmlf (original)
+++ incubator/roller/trunk/custom/custom-dbscripts.xmlf Thu Apr 20 09:22:49 2006
@@ -1,5 +1,5 @@
-<!-- Include SQL files in dbscript substitution
+<!-- Include SQL files in dbscript substitution, for example:
<fileset dir="./sandbox/planetroller/metadata/database">
<include name="createdb-planet-raw.sql" />
<include name="droptables-planet-raw.sql" />
Modified: incubator/roller/trunk/custom/custom-gen-beans.xmlf
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/custom/custom-gen-beans.xmlf?rev=395633&r1=395632&r2=395633&view=diff
==============================================================================
--- incubator/roller/trunk/custom/custom-gen-beans.xmlf (original)
+++ incubator/roller/trunk/custom/custom-gen-beans.xmlf Thu Apr 20 09:22:49 2006
@@ -1,8 +1,8 @@
-<!-- Custom classes to include in Hibernate mapping file generation
+<!-- Custom classes to include in Hibernate mapping file generation, for example:
<fileset dir="${basedir}/custom/src" includes="**/*Data.java" />
<fileset dir="${basedir}/sandbox/planetroller/src"
includes="**/*Data.java,**/*Assoc.java" />
-->
-
\ No newline at end of file
+
Modified: incubator/roller/trunk/custom/custom-gen-web.xmlf
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/custom/custom-gen-web.xmlf?rev=395633&r1=395632&r2=395633&view=diff
==============================================================================
--- incubator/roller/trunk/custom/custom-gen-web.xmlf (original)
+++ incubator/roller/trunk/custom/custom-gen-web.xmlf Thu Apr 20 09:22:49 2006
@@ -1,10 +1,9 @@
<!-- Include experimental protocols in build
-<fileset dir="${basedir}/sandbox/atomprotocol/src" includes="**/*Servlet.java" />
<fileset dir="${basedir}/sandbox/atomadminprotocol/src" includes="**/*Servlet.java" />
-->
-<!-- Custom classes to include in Struts and web generation
+<!-- Custom classes to include in Struts and web generation, for example:
<fileset dir="${basedir}/custom/src" includes="**/*Data.java" />
<fileset dir="${basedir}/sandbox/planetroller/src" includes="**/*Data.java" />
<fileset dir="${basedir}/sandbox/planetroller/src" includes="**/*Action.java" />
Modified: incubator/roller/trunk/custom/custom-jars.xmlf
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/custom/custom-jars.xmlf?rev=395633&r1=395632&r2=395633&view=diff
==============================================================================
--- incubator/roller/trunk/custom/custom-jars.xmlf (original)
+++ incubator/roller/trunk/custom/custom-jars.xmlf Thu Apr 20 09:22:49 2006
@@ -1,7 +1,4 @@
<!--
-Custom jars to be added to both rollerbeans and rollerweb compiles
--->
-
-<!-- Uncomment this to compile experimental JDO backend
+Custom jars to be added to both rollerbeans and rollerweb compiles, for example:
<fileset dir="${basedir}/sandbox/jdobackend/lib" includes="*.jar" />
- -->
+-->
Modified: incubator/roller/trunk/custom/custom-post-dbtest.xmlf
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/custom/custom-post-dbtest.xmlf?rev=395633&r1=395632&r2=395633&view=diff
==============================================================================
--- incubator/roller/trunk/custom/custom-post-dbtest.xmlf (original)
+++ incubator/roller/trunk/custom/custom-post-dbtest.xmlf Thu Apr 20 09:22:49 2006
@@ -1,5 +1,5 @@
-<!-- add post-test tasks to test-hibernate target of build.xml
+<!-- add post-test tasks to test-hibernate target of build.xml
<sql driver="org.hsqldb.jdbcDriver"
url="jdbc:hsqldb:hsql://localhost:3219"
userid="sa" password=""
Modified: incubator/roller/trunk/custom/custom-pre-dbtest.xmlf
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/custom/custom-pre-dbtest.xmlf?rev=395633&r1=395632&r2=395633&view=diff
==============================================================================
--- incubator/roller/trunk/custom/custom-pre-dbtest.xmlf (original)
+++ incubator/roller/trunk/custom/custom-pre-dbtest.xmlf Thu Apr 20 09:22:49 2006
@@ -6,4 +6,4 @@
src="./build/roller/WEB-INF/dbscripts/hsql/createdb-planet.sql"
classpath="${ro.tools}/buildtime/hsqldb.jar" />
-->
-
\ No newline at end of file
+
Modified: incubator/roller/trunk/custom/custom-src-beans.xmlf
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/custom/custom-src-beans.xmlf?rev=395633&r1=395632&r2=395633&view=diff
==============================================================================
--- incubator/roller/trunk/custom/custom-src-beans.xmlf (original)
+++ incubator/roller/trunk/custom/custom-src-beans.xmlf Thu Apr 20 09:22:49 2006
@@ -5,4 +5,4 @@
<!-- Uncomment this to compile experimental JDO backend
<src path="${basedir}/sandbox/jdobackend/src" />
- -->
\ No newline at end of file
+ -->
Modified: incubator/roller/trunk/custom/custom-src-web.xmlf
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/custom/custom-src-web.xmlf?rev=395633&r1=395632&r2=395633&view=diff
==============================================================================
--- incubator/roller/trunk/custom/custom-src-web.xmlf (original)
+++ incubator/roller/trunk/custom/custom-src-web.xmlf Thu Apr 20 09:22:49 2006
@@ -1,7 +1,6 @@
<!-- Include experimental Atom Protocol impl. in build.
<src path="${basedir}/sandbox/atomadminprotocol/src" />
-<src path="${basedir}/sandbox/atomprotocol/src" />
-->
Modified: incubator/roller/trunk/custom/custom-web.xmlf
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/custom/custom-web.xmlf?rev=395633&r1=395632&r2=395633&view=diff
==============================================================================
--- incubator/roller/trunk/custom/custom-web.xmlf (original)
+++ incubator/roller/trunk/custom/custom-web.xmlf Thu Apr 20 09:22:49 2006
@@ -1,13 +1,4 @@
-<!-- Need Atom PubControl Module for Atom protocol support
-<copy todir="${build.stage_web}/WEB-INF/classes" overwrite="true">
- <fileset dir="${basedir}/sandbox/atomprotocol">
- <include name="rome.properties" />
- </fileset>
-</copy>
--->
-
-
<!-- Example: copy custom files into the Roller web context
<copy todir="${build.stage_web}" overwrite="true">
<fileset dir="${basedir}/sandbox/planetroller/web">
Added: incubator/roller/trunk/src/org/roller/presentation/webservices/atomprotocol/AtomHandler.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/presentation/webservices/atomprotocol/AtomHandler.java?rev=395633&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/roller/presentation/webservices/atomprotocol/AtomHandler.java (added)
+++ incubator/roller/trunk/src/org/roller/presentation/webservices/atomprotocol/AtomHandler.java Thu Apr 20 09:22:49 2006
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2005 David M Johnson (For RSS and Atom In Action)
+ *
+ * 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.roller.presentation.webservices.atomprotocol;
+
+import java.io.InputStream;
+import java.util.Date;
+
+import com.sun.syndication.feed.atom.Entry;
+import com.sun.syndication.feed.atom.Feed;
+
+/**
+ * Interface to be supported by an Atom server, expected lifetime: one request.
+ * AtomServlet calls this generic interface instead of Roller specific APIs.
+ * <p />
+ * Designed to be Roller independent.
+ *
+ * @author David M Johnson
+ */
+public interface AtomHandler
+{
+ /** Get username of authenticated user */
+ public String getAuthenticatedUsername();
+
+ /**
+ * Return introspection document
+ */
+ public AtomService getIntrospection(String[] pathInfo) throws Exception;
+
+ /**
+ * Return collection
+ * @param pathInfo Used to determine which collection and range
+ */
+ public Feed getCollection(String[] pathInfo) throws Exception;
+
+ /**
+ * Create a new entry specified by pathInfo and posted entry.
+ * @param pathInfo Path info portion of URL
+ */
+ public Entry postEntry(String[] pathInfo, Entry entry) throws Exception;
+
+ /**
+ * Get entry specified by pathInfo.
+ * @param pathInfo Path info portion of URL
+ */
+ public Entry getEntry(String[] pathInfo) throws Exception;
+
+ /**
+ * Update entry specified by pathInfo and posted entry.
+ * @param pathInfo Path info portion of URL
+ */
+ public Entry putEntry(String[] pathInfo, Entry entry) throws Exception;
+
+ /**
+ * Delete entry specified by pathInfo.
+ * @param pathInfo Path info portion of URL
+ */
+ public void deleteEntry(String[] pathInfo) throws Exception;
+
+ /**
+ * Create a new resource specified by pathInfo, contentType, and binary data
+ * @param pathInfo Path info portion of URL
+ * @param contentType MIME type of uploaded content
+ * @param data Binary data representing uploaded content
+ */
+ public Entry postMedia(String[] pathInfo, String name, String contentType,
+ InputStream is) throws Exception;
+
+ /**
+ * Update a resource.
+ * @param pathInfo Path info portion of URL
+ */
+ public Entry putMedia(String[] pathInfo, String contentType,
+ InputStream is) throws Exception;
+
+ /**
+ * Delete resource specified by pathInfo.
+ * @param pathInfo Path info portion of URL
+ */
+ public void deleteMedia(String[] pathInfo) throws Exception;
+
+ /**
+ * Get resrouce entry.
+ * @param pathInfo Path info portion of URL
+ */
+ public Entry getMedia(String[] pathInfo) throws Exception;
+
+ public boolean isIntrospectionURI(String [] pathInfo);
+
+ public boolean isCollectionURI(String [] pathInfo);
+ public boolean isEntryCollectionURI(String [] pathInfo);
+ public boolean isMediaCollectionURI(String [] pathInfo);
+
+ public boolean isEntryURI(String[] pathInfo);
+ public boolean isMediaURI(String[] pathInfo);
+}
+
Added: incubator/roller/trunk/src/org/roller/presentation/webservices/atomprotocol/AtomService.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/presentation/webservices/atomprotocol/AtomService.java?rev=395633&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/roller/presentation/webservices/atomprotocol/AtomService.java (added)
+++ incubator/roller/trunk/src/org/roller/presentation/webservices/atomprotocol/AtomService.java Thu Apr 20 09:22:49 2006
@@ -0,0 +1,247 @@
+/*
+ * Copyright 2005 David M Johnson (For RSS and Atom In Action)
+ *
+ * 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.roller.presentation.webservices.atomprotocol;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.jdom.Document;
+import org.jdom.Element;
+import org.jdom.Namespace;
+import org.jdom.filter.Filter;
+
+/**
+ * This class models an Atom workspace.
+ * @author Dave Johnson
+ */
+/* Based on: draft-ietf-atompub-protocol-08.txt
+ *
+ * appService =
+ * element app:service {
+ * appCommonAttributes,
+ * ( appWorkspace+
+ * & extensionElement* )
+ * }
+ *
+ * Here is an example Atom workspace:
+ *
+ * <?xml version="1.0" encoding='utf-8'?>
+ * <service xmlns="http://purl.org/atom/app#">
+ * <workspace title="Main Site" >
+ * <collection
+ * title="My Blog Entries"
+ * href="http://example.org/reilly/main" >
+ * <member-type>entry</member-type>
+ * </collection>
+ * <collection
+ * title="Pictures"
+ * href="http://example.org/reilly/pic" >
+ * <member-type>media</member-type>
+ * </collection>
+ * </workspace>
+ * </service>
+ */
+public class AtomService {
+ public static final Namespace ns =
+ Namespace.getNamespace("http://purl.org/atom/app#");
+
+ private List workspaces = new ArrayList();
+
+ public AtomService() {
+ }
+
+ public void addWorkspace(AtomService.Workspace workspace) {
+ workspaces.add(workspace);
+ }
+
+ public List getWorkspaces() {
+ return workspaces;
+ }
+
+ public void setWorkspaces(List workspaces) {
+ this.workspaces = workspaces;
+ }
+
+ /**
+ * This class models an Atom workspace.
+ *
+ * @author Dave Johnson
+ */
+ /*
+ * appWorkspace = element app:workspace { attribute title { text }, (
+ * appCollection* & anyElement* ) }
+ */
+ public static class Workspace {
+ private String title = null;
+ private List collections = new ArrayList();
+
+ public Workspace() {
+ }
+
+ public List getCollections() {
+ return collections;
+ }
+
+ public void setCollections(List collections) {
+ this.collections = collections;
+ }
+
+ public void addCollection(AtomService.Collection col) {
+ collections.add(col);
+ }
+
+ /** Workspace must have a human readable title */
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+ }
+
+ /**
+ * This class models an Atom workspace collection.
+ * @author Dave Johnson
+ */
+ /* appCollection =
+ * element app:collection {
+ * appCommonAttributes,
+ * attribute title { text },
+ * attribute href { text },
+ * ( appMemberType
+ * & appListTemplate
+ * & extensionElement* )
+ * }
+ */
+ public static class Collection {
+ private String title = null;
+ private String memberType = "entry"; // or "media"
+ private String listTemplate = null;
+ private String href = null;
+
+ public Collection() {
+ }
+
+ /**
+ * Member type May be "entry" or "media".
+ */
+ public String getMemberType() {
+ return memberType;
+ }
+
+ public void setMemberType(String memberType) {
+ this.memberType = memberType;
+ }
+
+ /** The URI of the collection */
+ public String getHref() {
+ return href;
+ }
+
+ public void setHref(String href) {
+ this.href = href;
+ }
+
+ /** Must have human readable title */
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+ }
+
+ /** Deserialize an Atom service XML document into an object */
+ public static AtomService documentToService(Document document) {
+ AtomService service = new AtomService();
+ Element root = document.getRootElement();
+ List spaces = root.getChildren("workspace", ns);
+ Iterator iter = spaces.iterator();
+ while (iter.hasNext()) {
+ Element e = (Element) iter.next();
+ service.addWorkspace(AtomService.elementToWorkspace(e));
+ }
+ return service;
+ }
+
+ /** Serialize an AtomService object into an XML document */
+ public static Document serviceToDocument(AtomService service) {
+ Document doc = new Document();
+ Element root = new Element("service", ns);
+ doc.setRootElement(root);
+ Iterator iter = service.getWorkspaces().iterator();
+ while (iter.hasNext()) {
+ AtomService.Workspace space = (AtomService.Workspace) iter.next();
+ root.addContent(AtomService.workspaceToElement(space));
+ }
+ return doc;
+ }
+
+ /** Deserialize a Atom workspace XML element into an object */
+ public static AtomService.Workspace elementToWorkspace(Element element) {
+ AtomService.Workspace space = new AtomService.Workspace();
+ space.setTitle(element.getAttribute("title").getValue());
+ List collections = element.getChildren("collection", ns);
+ Iterator iter = collections.iterator();
+ while (iter.hasNext()) {
+ Element e = (Element) iter.next();
+ space.addCollection(AtomService.elementToCollection(e));
+ }
+ return space;
+ }
+
+ /** Serialize an AtomService.Workspace object into an XML element */
+ public static Element workspaceToElement(Workspace space) {
+ Namespace ns = Namespace.getNamespace("http://purl.org/atom/app#");
+ Element element = new Element("workspace", ns);
+ element.setAttribute("title", space.getTitle());
+ Iterator iter = space.getCollections().iterator();
+ while (iter.hasNext()) {
+ AtomService.Collection col = (AtomService.Collection) iter.next();
+ element.addContent(collectionToElement(col));
+ }
+ return element;
+ }
+
+ /** Deserialize an Atom service collection XML element into an object */
+ public static AtomService.Collection elementToCollection(Element element) {
+ AtomService.Collection collection = new AtomService.Collection();
+ collection.setTitle(element.getAttribute("title").getValue());
+ collection.setHref(element.getAttribute("href").getValue());
+ Element memberType = element.getChild("member-type", ns);
+ if (memberType != null) {
+ collection.setMemberType(memberType.getText());
+ }
+ return collection;
+ }
+
+ /** Serialize an AtomService.Collection object into an XML element */
+ public static Element collectionToElement(AtomService.Collection collection) {
+ Namespace ns = Namespace.getNamespace("http://purl.org/atom/app#");
+ Element element = new Element("collection", ns);
+ element.setAttribute("title", collection.getTitle());
+ element.setAttribute("href", collection.getHref());
+
+ Element memberType = new Element("member-type", ns);
+ memberType.setText(collection.getMemberType());
+ element.addContent(memberType);
+
+ return element;
+ }
+}
\ No newline at end of file
Added: incubator/roller/trunk/src/org/roller/presentation/webservices/atomprotocol/AtomServlet.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/presentation/webservices/atomprotocol/AtomServlet.java?rev=395633&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/roller/presentation/webservices/atomprotocol/AtomServlet.java (added)
+++ incubator/roller/trunk/src/org/roller/presentation/webservices/atomprotocol/AtomServlet.java Thu Apr 20 09:22:49 2006
@@ -0,0 +1,375 @@
+/*
+ * Copyright 2005 David M Johnson (For RSS and Atom In Action)
+ *
+ * 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.roller.presentation.webservices.atomprotocol;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.jdom.Document;
+import org.jdom.Element;
+import org.jdom.JDOMException;
+import org.jdom.input.SAXBuilder;
+import org.jdom.output.Format;
+import org.jdom.output.XMLOutputter;
+
+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.io.FeedException;
+import com.sun.syndication.io.WireFeedInput;
+import com.sun.syndication.io.WireFeedOutput;
+import java.io.StringWriter;
+import org.jdom.Namespace;
+import org.roller.config.RollerConfig;
+
+/**
+ * Atom Servlet implements Atom by calling a Roller independent handler.
+ * @web.servlet name="AtomServlet"
+ * @web.servlet-mapping url-pattern="/app/*"
+ * @author David M Johnson
+ */
+public class AtomServlet extends HttpServlet {
+ public static final String FEED_TYPE = "atom_1.0";
+
+ private static Log mLogger =
+ LogFactory.getFactory().getInstance(AtomServlet.class);
+
+ //-----------------------------------------------------------------------------
+ /**
+ * Create an Atom request handler.
+ * TODO: make AtomRequestHandler implementation configurable.
+ */
+ private AtomHandler createAtomRequestHandler(HttpServletRequest request)
+ throws ServletException {
+ boolean enabled = RollerConfig.getBooleanProperty(
+ "webservices.atomprotocol.enable");
+ if (!enabled) {
+ throw new ServletException("ERROR: Atom protocol not enabled");
+ }
+ return new RollerAtomHandler(request);
+ }
+
+ //-----------------------------------------------------------------------------
+ /**
+ * Handles an Atom GET by calling handler and writing results to response.
+ */
+ protected void doGet(HttpServletRequest req, HttpServletResponse res)
+ throws ServletException, IOException {
+ AtomHandler handler = createAtomRequestHandler(req);
+ String userName = handler.getAuthenticatedUsername();
+ if (userName != null) {
+ String[] pathInfo = getPathInfo(req);
+ try {
+ if (handler.isIntrospectionURI(pathInfo)) {
+ // return an Atom Service document
+ AtomService service = handler.getIntrospection(pathInfo);
+ Document doc = AtomService.serviceToDocument(service);
+ res.setContentType("application/xml; charset=utf8");
+ Writer writer = res.getWriter();
+ XMLOutputter outputter = new XMLOutputter();
+ outputter.setFormat(Format.getPrettyFormat());
+ outputter.output(doc, writer);
+ writer.close();
+ res.setStatus(HttpServletResponse.SC_OK);
+ } else if (handler.isCollectionURI(pathInfo)) {
+ // return a collection
+ Feed col = handler.getCollection(pathInfo);
+ col.setFeedType(FEED_TYPE);
+ WireFeedOutput wireFeedOutput = new WireFeedOutput();
+ Document feedDoc = wireFeedOutput.outputJDom(col);
+ res.setContentType("application/atom+xml; charset=utf8");
+ Writer writer = res.getWriter();
+ XMLOutputter outputter = new XMLOutputter();
+ outputter.setFormat(Format.getPrettyFormat());
+ outputter.output(feedDoc, writer);
+ writer.close();
+ res.setStatus(HttpServletResponse.SC_OK);
+ } else if (handler.isEntryURI(pathInfo)) {
+ // return an entry
+ Entry entry = handler.getEntry(pathInfo);
+ if (entry != null) {
+ Writer writer = res.getWriter();
+ res.setContentType("application/atom+xml; charset=utf8");
+ serializeEntry(entry, writer);
+ writer.close();
+ } else {
+ res.setStatus(HttpServletResponse.SC_NOT_FOUND);
+ }
+ } else if (handler.isMediaURI(pathInfo)) {
+ // return a resource entry
+ Entry entry = handler.getMedia(pathInfo);
+ if (entry != null) {
+ Writer writer = res.getWriter();
+ res.setContentType("application/atom+xml; charset=utf8");
+ serializeEntry(entry, writer);
+ writer.close();
+ } else {
+ res.setStatus(HttpServletResponse.SC_NOT_FOUND);
+ }
+ } else {
+ res.setStatus(HttpServletResponse.SC_NOT_FOUND);
+ }
+ } catch (Exception e) {
+ res.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ e.printStackTrace(res.getWriter());
+ mLogger.error(e);
+ }
+ } else {
+ res.setHeader("WWW-Authenticate", "BASIC realm=\"Roller\"");
+ res.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+ }
+ }
+
+ //-----------------------------------------------------------------------------
+ /**
+ * Handles an Atom POST by calling handler to identify URI, reading/parsing
+ * data, calling handler and writing results to response.
+ */
+ protected void doPost(HttpServletRequest req, HttpServletResponse res)
+ throws ServletException, IOException {
+ AtomHandler handler = createAtomRequestHandler(req);
+ String userName = handler.getAuthenticatedUsername();
+ if (userName != null) {
+ String[] pathInfo = getPathInfo(req);
+ try {
+ if (handler.isEntryCollectionURI(pathInfo)) {
+
+ // parse incoming entry
+ Entry unsavedEntry = parseEntry(
+ new InputStreamReader(req.getInputStream()));
+
+ // call handler to post it
+ Entry savedEntry = handler.postEntry(pathInfo, unsavedEntry);
+ Iterator links = savedEntry.getAlternateLinks().iterator();
+
+ // return alternate link as Location header
+ while (links.hasNext()) {
+ Link link = (Link) links.next();
+ if (link.getRel().equals("alternate") || link.getRel() == null) {
+ res.addHeader("Location", link.getHref());
+ break;
+ }
+ }
+ // write entry back out to response
+ res.setStatus(HttpServletResponse.SC_CREATED);
+ res.setContentType("application/atom+xml; charset=utf8");
+ Writer writer = res.getWriter();
+ serializeEntry(savedEntry, writer);
+ writer.close();
+ } else if (handler.isMediaCollectionURI(pathInfo)) {
+ // get incoming file name from HTTP header
+ String name = req.getHeader("Name");
+
+ // hand input stream of to hander to post file
+ Entry resource = handler.postMedia(
+ pathInfo, name, req.getContentType(), req.getInputStream());
+ res.setStatus(HttpServletResponse.SC_CREATED);
+ com.sun.syndication.feed.atom.Content content =
+ (com.sun.syndication.feed.atom.Content)resource.getContents().get(0);
+ res.setHeader("Location", content.getSrc());
+ Writer writer = res.getWriter();
+ serializeEntry(resource, writer);
+ writer.close();
+ } else {
+ res.setStatus(HttpServletResponse.SC_NOT_FOUND);
+ }
+ } catch (Exception e) {
+ res.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ e.printStackTrace(res.getWriter());
+ mLogger.error(e);
+ }
+ } else {
+ res.setHeader("WWW-Authenticate", "BASIC realm=\"Roller\"");
+ res.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+ }
+ }
+
+ //-----------------------------------------------------------------------------
+ /**
+ * Handles an Atom PUT by calling handler to identify URI, reading/parsing
+ * data, calling handler and writing results to response.
+ */
+ protected void doPut(HttpServletRequest req, HttpServletResponse res)
+ throws ServletException, IOException {
+ AtomHandler handler = createAtomRequestHandler(req);
+ String userName = handler.getAuthenticatedUsername();
+ if (userName != null) {
+ String[] pathInfo = getPathInfo(req);
+ try {
+ if (handler.isEntryURI(pathInfo)) {
+ // parse incoming entry
+ Entry unsavedEntry = parseEntry(
+ new InputStreamReader(req.getInputStream()));
+
+ // call handler to put entry
+ Entry updatedEntry = handler.putEntry(pathInfo, unsavedEntry);
+
+ // write entry back out to response
+ res.setContentType("application/atom+xml; charset=utf8");
+ Writer writer = res.getWriter();
+ serializeEntry(updatedEntry, writer);
+ res.setStatus(HttpServletResponse.SC_OK);
+ writer.close();
+ } else if (handler.isMediaURI(pathInfo)) {
+ // hand input stream to handler
+ Entry updatedEntry = handler.putMedia(
+ pathInfo, req.getContentType(), req.getInputStream());
+
+ // write entry back out to response
+ res.setContentType("application/atom+xml; charset=utf8");
+ Writer writer = res.getWriter();
+ serializeEntry(updatedEntry, writer);
+ writer.close();
+ res.setStatus(HttpServletResponse.SC_OK);
+ } else {
+ res.setStatus(HttpServletResponse.SC_NOT_FOUND);
+ }
+ } catch (Exception e) {
+ res.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ e.printStackTrace(res.getWriter());
+ mLogger.error(e);
+ }
+ } else {
+ res.setHeader("WWW-Authenticate", "BASIC realm=\"Roller\"");
+ res.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+ }
+ }
+
+ //-----------------------------------------------------------------------------
+ /**
+ * Handle Atom DELETE by calling appropriate handler.
+ */
+ protected void doDelete(HttpServletRequest req, HttpServletResponse res)
+ throws ServletException, IOException {
+ AtomHandler handler = createAtomRequestHandler(req);
+ String userName = handler.getAuthenticatedUsername();
+ if (userName != null) {
+ String[] pathInfo = getPathInfo(req);
+ try {
+ if (handler.isEntryURI(pathInfo)) {
+ handler.deleteEntry(pathInfo);
+ res.setStatus(HttpServletResponse.SC_OK);
+ } else if (handler.isMediaURI(pathInfo)) {
+ handler.deleteMedia(pathInfo);
+ res.setStatus(HttpServletResponse.SC_OK);
+ } else {
+ res.setStatus(HttpServletResponse.SC_NOT_FOUND);
+ }
+ } catch (Exception e) {
+ res.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ e.printStackTrace(res.getWriter());
+ mLogger.error(e);
+ }
+ } else {
+ res.setHeader("WWW-Authenticate", "BASIC realm=\"Roller\"");
+ res.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+ }
+ }
+
+ //-----------------------------------------------------------------------------
+ /**
+ * Convenience method to return the PathInfo from the request.
+ */
+ protected String[] getPathInfo(HttpServletRequest request) {
+ String mPathInfo = request.getPathInfo();
+ mPathInfo = (mPathInfo!=null) ? mPathInfo : "";
+ return StringUtils.split(mPathInfo,"/");
+ }
+
+ /**
+ * Serialize entry to writer.
+ */
+ public static void serializeEntry(Entry entry, Writer writer)
+ throws IllegalArgumentException, FeedException, IOException {
+ // Build a feed containing only the entry
+ List entries = new ArrayList();
+ entries.add(entry);
+ Feed feed1 = new Feed();
+ feed1.setFeedType(AtomServlet.FEED_TYPE);
+ feed1.setEntries(entries);
+
+ // Get Rome to output feed as a JDOM document
+ WireFeedOutput wireFeedOutput = new WireFeedOutput();
+ Document feedDoc = wireFeedOutput.outputJDom(feed1);
+
+ // Grab entry element from feed and get JDOM to serialize it
+ Element entryElement= (Element)feedDoc.getRootElement().getChildren().get(0);
+
+ // Add our own namespaced element, so we can determine if we can
+ // count on client to preserve foreign markup as it should.
+ Element rollerElement = new Element("atom-draft",
+ "http://rollerweblogger.org/namespaces/app");
+ rollerElement.setText("8");
+ entryElement.addContent(rollerElement);
+
+ XMLOutputter outputter = new XMLOutputter();
+ outputter.setFormat(Format.getPrettyFormat());
+
+ if (mLogger.isDebugEnabled()) {
+ StringWriter sw = new StringWriter();
+ outputter.output(entryElement, sw);
+ mLogger.debug(sw.toString());
+ writer.write(sw.toString());
+ } else {
+ outputter.output(entryElement, writer);
+ }
+ }
+
+ /**
+ * Parse entry from reader.
+ */
+ public static Entry parseEntry(Reader rd)
+ throws JDOMException, IOException, IllegalArgumentException, FeedException {
+ // Parse entry into JDOM tree
+ SAXBuilder builder = new SAXBuilder();
+ Document entryDoc = builder.build(rd);
+ Element fetchedEntryElement = entryDoc.getRootElement();
+ fetchedEntryElement.detach();
+
+ // Put entry into a JDOM document with 'feed' root so that Rome can handle it
+ Feed feed = new Feed();
+ feed.setFeedType(FEED_TYPE);
+ WireFeedOutput wireFeedOutput = new WireFeedOutput();
+ Document feedDoc = wireFeedOutput.outputJDom(feed);
+ feedDoc.getRootElement().addContent(fetchedEntryElement);
+
+ // Check for our special namespaced element. If it's there, then we
+ // know that client is not preserving foreign markup.
+ Namespace ns = Namespace.getNamespace(
+ "http://rollerweblogger.org/namespaces/app");
+ Element rollerElement = fetchedEntryElement.getChild("atom-draft", ns);
+ if (rollerElement == null) {
+ mLogger.debug("Client is NOT preserving foreign markup");
+ }
+
+ WireFeedInput input = new WireFeedInput();
+ Feed parsedFeed = (Feed)input.build(feedDoc);
+ return (Entry)parsedFeed.getEntries().get(0);
+ }
+}
Added: incubator/roller/trunk/src/org/roller/presentation/webservices/atomprotocol/PubControlModule.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/presentation/webservices/atomprotocol/PubControlModule.java?rev=395633&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/roller/presentation/webservices/atomprotocol/PubControlModule.java (added)
+++ incubator/roller/trunk/src/org/roller/presentation/webservices/atomprotocol/PubControlModule.java Thu Apr 20 09:22:49 2006
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2004 Sun Microsystems, Inc.
+ *
+ * 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.roller.presentation.webservices.atomprotocol;
+
+import com.sun.syndication.feed.module.Module;
+
+public interface PubControlModule extends Module {
+ public static final String URI = "http://purl.org/atom/app#";
+ public Boolean getDraft();
+ public void setDraft(Boolean draft);
+}
Added: incubator/roller/trunk/src/org/roller/presentation/webservices/atomprotocol/PubControlModuleGenerator.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/presentation/webservices/atomprotocol/PubControlModuleGenerator.java?rev=395633&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/roller/presentation/webservices/atomprotocol/PubControlModuleGenerator.java (added)
+++ incubator/roller/trunk/src/org/roller/presentation/webservices/atomprotocol/PubControlModuleGenerator.java Thu Apr 20 09:22:49 2006
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2004 Sun Microsystems, Inc.
+ *
+ * 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.roller.presentation.webservices.atomprotocol;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.jdom.Element;
+import org.jdom.Namespace;
+
+import com.sun.syndication.feed.module.Module;
+import com.sun.syndication.io.ModuleGenerator;
+
+public class PubControlModuleGenerator implements ModuleGenerator {
+ private static final Namespace PUBCONTROL_NS =
+ Namespace.getNamespace("app", PubControlModule.URI);
+
+ public String getNamespaceUri() {
+ return PubControlModule.URI;
+ }
+
+ private static final Set NAMESPACES;
+
+ static {
+ Set nss = new HashSet();
+ nss.add(PUBCONTROL_NS);
+ NAMESPACES = Collections.unmodifiableSet(nss);
+ }
+
+ public Set getNamespaces() {
+ return NAMESPACES;
+ }
+
+ public void generate(Module module, Element element) {
+ PubControlModule m = (PubControlModule)module;
+ if (m.getDraft() != null) {
+ String draft = m.getDraft().booleanValue() ? "yes" : "no";
+ Element control = new Element("control", PUBCONTROL_NS);
+ control.addContent(generateSimpleElement("draft", draft));
+ element.addContent(control);
+ }
+ }
+
+ protected Element generateSimpleElement(String name, String value) {
+ Element element = new Element(name, PUBCONTROL_NS);
+ element.addContent(value);
+ return element;
+ }
+
+}
Added: incubator/roller/trunk/src/org/roller/presentation/webservices/atomprotocol/PubControlModuleImpl.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/presentation/webservices/atomprotocol/PubControlModuleImpl.java?rev=395633&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/roller/presentation/webservices/atomprotocol/PubControlModuleImpl.java (added)
+++ incubator/roller/trunk/src/org/roller/presentation/webservices/atomprotocol/PubControlModuleImpl.java Thu Apr 20 09:22:49 2006
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2004 Sun Microsystems, Inc.
+ *
+ * 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.roller.presentation.webservices.atomprotocol;
+
+import com.sun.syndication.feed.module.ModuleImpl;
+
+public class PubControlModuleImpl extends ModuleImpl implements PubControlModule {
+ private Boolean _draft;
+
+ public PubControlModuleImpl() {
+ super(PubControlModule.class,PubControlModule.URI);
+ }
+ public Boolean getDraft() {
+ return _draft;
+ }
+ public void setDraft(Boolean draft) {
+ _draft = draft;
+ }
+ public Class getInterface() {
+ return PubControlModule.class;
+ }
+ public void copyFrom(Object obj) {
+ PubControlModule m = (PubControlModule)obj;
+ setDraft(m.getDraft());
+ }
+}
Added: incubator/roller/trunk/src/org/roller/presentation/webservices/atomprotocol/PubControlModuleParser.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/presentation/webservices/atomprotocol/PubControlModuleParser.java?rev=395633&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/roller/presentation/webservices/atomprotocol/PubControlModuleParser.java (added)
+++ incubator/roller/trunk/src/org/roller/presentation/webservices/atomprotocol/PubControlModuleParser.java Thu Apr 20 09:22:49 2006
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2004 Sun Microsystems, Inc.
+ *
+ * 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.roller.presentation.webservices.atomprotocol;
+
+import org.jdom.Element;
+import org.jdom.Namespace;
+
+import com.sun.syndication.feed.module.Module;
+import com.sun.syndication.io.ModuleParser;
+
+public class PubControlModuleParser implements ModuleParser {
+
+ public String getNamespaceUri() {
+ return PubControlModule.URI;
+ }
+
+ public Namespace getContentNamespace() {
+ return Namespace.getNamespace(PubControlModule.URI);
+ }
+ public Module parse(Element elem) {
+ boolean foundSomething = false;
+ PubControlModule m = new PubControlModuleImpl();
+ Element e = elem.getChild("control", getContentNamespace());
+ if (e != null) {
+ Element draftElem = e.getChild("draft", getContentNamespace());
+ if (draftElem != null) {
+ if ("yes".equals(draftElem.getText())) m.setDraft(Boolean.TRUE);
+ if ("no".equals(draftElem.getText())) m.setDraft(Boolean.FALSE);
+ }
+ }
+ return m.getDraft()!=null ? m : null;
+ }
+}
+
Added: incubator/roller/trunk/src/org/roller/presentation/webservices/atomprotocol/RollerAtomHandler.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/presentation/webservices/atomprotocol/RollerAtomHandler.java?rev=395633&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/roller/presentation/webservices/atomprotocol/RollerAtomHandler.java (added)
+++ incubator/roller/trunk/src/org/roller/presentation/webservices/atomprotocol/RollerAtomHandler.java Thu Apr 20 09:22:49 2006
@@ -0,0 +1,828 @@
+/*
+ * Copyright 2005 David M Johnson (For RSS and Atom In Action)
+ *
+ * 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.roller.presentation.webservices.atomprotocol;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.sql.Timestamp;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+import java.util.StringTokenizer;
+import java.util.Collections;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.roller.model.FileManager;
+import org.roller.model.Roller;
+import org.roller.model.RollerFactory;
+import org.roller.pojos.UserData;
+import org.roller.pojos.PermissionsData;
+import org.roller.pojos.WeblogCategoryData;
+import org.roller.pojos.WeblogEntryData;
+import org.roller.pojos.WebsiteData;
+import org.roller.presentation.RollerContext;
+import org.roller.util.RollerMessages;
+import org.roller.util.Utilities;
+
+import com.sun.syndication.feed.atom.Content;
+import com.sun.syndication.feed.atom.Category;
+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 javax.activation.FileTypeMap;
+import org.roller.RollerException;
+import org.roller.config.RollerConfig;
+import org.roller.model.WeblogManager;
+import org.roller.presentation.cache.CacheManager;
+
+/**
+ * Roller's Atom Protocol implementation.
+ * <pre>
+ * Here are the URIs suppored:
+ *
+ * URI type URI form Handled by
+ * -------- -------- ----------
+ * Introspection URI / getIntrosection()
+ * Collection URI /blog-name/<collection-name> getCollection()
+ * Member URI /blog-name/<object-name> post<object-name>()
+ * Member URI /blog-name/<object-name>/id get<object-name>()
+ * Member URI /blog-name/<object-name>/id put<object-name>()
+ * Member URI /blog-name/<object-name>/id delete<object-name>()
+ *
+ * Until group blogging is supported weblogHandle == blogname.
+ *
+ * Collection-names Object-names
+ * ---------------- ------------
+ * entries entry
+ * resources resource
+ * </pre>
+ *
+ * @author David M Johnson
+ */
+public class RollerAtomHandler implements AtomHandler {
+ private HttpServletRequest mRequest;
+ private Roller mRoller;
+ private RollerContext mRollerContext;
+ private UserData user;
+ private int mMaxEntries = 20;
+ //private MessageDigest md5Helper = null;
+ //private MD5Encoder md5Encoder = new MD5Encoder();
+
+ private static Log mLogger =
+ LogFactory.getFactory().getInstance(RollerAtomHandler.class);
+
+ //---------------------------------------------------------------- construction
+
+ /**
+ * Create Atom handler for a request and attempt to authenticate user.
+ * If user is authenticated, then getAuthenticatedUsername() will return
+ * then user's name, otherwise it will return null.
+ */
+ public RollerAtomHandler(HttpServletRequest request) {
+ mRequest = request;
+ mRoller = RollerFactory.getRoller();
+ mRollerContext = RollerContext.getRollerContext();
+
+ // TODO: decide what to do about authentication, is WSSE going to fly?
+ //mUsername = authenticateWSSE(request);
+ String mUsername = authenticateBASIC(request);
+
+ if (mUsername != null) {
+ try {
+ this.user = mRoller.getUserManager().getUserByUsername(mUsername);
+ } catch (Exception e) {
+ mLogger.error("ERROR: setting user", e);
+ }
+ }
+ }
+
+ /**
+ * Return weblogHandle of authenticated user or null if there is none.
+ */
+ public String getAuthenticatedUsername() {
+ return this.user.getUserName();
+ }
+
+ //---------------------------------------------------------------- introspection
+
+ /**
+ * Return Atom service document for site, getting blog-name from pathInfo.
+ * The workspace will contain collections for entries, categories and resources.
+ */
+ public AtomService getIntrospection(String[] pathInfo) throws Exception {
+ if (pathInfo.length == 0) {
+ String absUrl = mRollerContext.getAbsoluteContextUrl(mRequest);
+ AtomService service = new AtomService();
+ List perms = mRoller.getUserManager().getAllPermissions(user);
+ if (perms != null) {
+ for (Iterator iter=perms.iterator(); iter.hasNext();) {
+ PermissionsData perm = (PermissionsData)iter.next();
+ String handle = perm.getWebsite().getHandle();
+ AtomService.Workspace workspace = new AtomService.Workspace();
+ workspace.setTitle(Utilities.removeHTML(perm.getWebsite().getName()));
+ service.addWorkspace(workspace);
+
+ AtomService.Collection entryCol = new AtomService.Collection();
+ entryCol.setTitle("Weblog Entries");
+ entryCol.setMemberType("entry");
+ entryCol.setHref(absUrl + "/app/"+handle+"/entries");
+ workspace.addCollection(entryCol);
+
+ AtomService.Collection uploadCol = new AtomService.Collection();
+ uploadCol.setTitle("Media Files");
+ uploadCol.setMemberType("media");
+ uploadCol.setHref(absUrl + "/app/"+handle+"/resources");
+ workspace.addCollection(uploadCol);
+ }
+ }
+ return service;
+ }
+ throw new Exception("ERROR: bad URL in getIntrospection()");
+ }
+
+ //----------------------------------------------------------------- collections
+
+ /**
+ * Return collection specified by pathinfo.
+ * <pre>
+ * Supports these URI forms:
+ * /<blog-name>/entries
+ * /<blog-name>/entries/offset
+ * /<blog-name>/resources
+ * /<blog-name>/resources/offset
+ * </pre>
+ */
+ public Feed getCollection(String[] pathInfo) throws Exception {
+ int start = 0;
+ if (pathInfo.length > 2) {
+ try {
+ String s = pathInfo[2].trim();
+ start = Integer.parseInt(s);
+ } catch (Throwable t) {
+ mLogger.warn("Unparsable range: " + pathInfo[2]);
+ }
+ }
+ if (pathInfo.length > 0 && pathInfo[1].equals("entries")) {
+ return getCollectionOfEntries(pathInfo, start, mMaxEntries);
+ } else if (pathInfo.length > 0 && pathInfo[1].equals("resources")) {
+ return getCollectionOfResources(pathInfo, start, mMaxEntries);
+ }
+ throw new Exception("ERROR: bad URL in getCollection()");
+ }
+
+ /**
+ * Helper method that returns collection of entries, called by getCollection().
+ */
+ public Feed getCollectionOfEntries(
+ String[] pathInfo, int start, int max) throws Exception {
+ String handle = pathInfo[0];
+ String absUrl = mRollerContext.getAbsoluteContextUrl(mRequest);
+ WebsiteData website = mRoller.getUserManager().getWebsiteByHandle(handle);
+ List entries = null;
+ if (canView(website)) {
+ entries = mRoller.getWeblogManager().getWeblogEntries(
+ website, // website
+ null, // startDate
+ null, // endDate
+ null, // catName
+ null, // status
+ "updateTime", // sortby
+ start, // offset (for range paging)
+ max + 1); // maxEntries
+ Feed feed = new Feed();
+ feed.setTitle("Entries for blog[" + handle + "]");
+ List atomEntries = new ArrayList();
+ int count = 0;
+ for (Iterator iter = entries.iterator(); iter.hasNext() && count < mMaxEntries; count++) {
+ WeblogEntryData rollerEntry = (WeblogEntryData)iter.next();
+ atomEntries.add(createAtomEntry(rollerEntry));
+ }
+ List links = new ArrayList();
+ if (entries.size() > max) { // add next link
+ int nextOffset = start + max;
+ String url = absUrl + "/app/" + 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 = absUrl + "/app/" +website.getHandle() + "/entries/" + prevOffset;
+ Link prevLink = new Link();
+ prevLink.setRel("previous");
+ prevLink.setHref(url);
+ links.add(prevLink);
+ }
+ if (links.size() > 0) feed.setOtherLinks(links);
+ feed.setEntries(atomEntries);
+ return feed;
+ }
+ throw new Exception("ERROR: not authorized");
+ }
+
+ /**
+ * Helper method that returns collection of resources, called by getCollection().
+ */
+ public Feed getCollectionOfResources(
+ String[] pathInfo, int start, int max) throws Exception {
+ String handle = pathInfo[0];
+ String absUrl = mRollerContext.getAbsoluteContextUrl(mRequest);
+ WebsiteData website = mRoller.getUserManager().getWebsiteByHandle(handle);
+ FileManager fmgr = mRoller.getFileManager();
+ File[] files = fmgr.getFiles(website.getHandle());
+ if (canView(website)) {
+ Feed feed = new Feed();
+ List atomEntries = new ArrayList();
+ int count = 0;
+ if (files != null && start < files.length) {
+ for (int i=start; i<(start + max) && i<(files.length); i++) {
+ Entry entry = createAtomResourceEntry(website, files[i]);
+ atomEntries.add(entry);
+ count++;
+ }
+ }
+ if (start + count < files.length) { // add next link
+ int nextOffset = start + max;
+ String url = absUrl + "/app/" + website.getHandle() + "/resources/" + nextOffset;
+ Link nextLink = new Link();
+ nextLink.setRel("next");
+ nextLink.setHref(url);
+ List next = new ArrayList();
+ next.add(nextLink);
+ feed.setOtherLinks(next);
+ }
+ if (start > 0) { // add previous link
+ int prevOffset = start > max ? start - max : 0;
+ String url = absUrl + "/app/" +website.getHandle() + "/resources/" + prevOffset;
+ Link prevLink = new Link();
+ prevLink.setRel("previous");
+ prevLink.setHref(url);
+ List prev = new ArrayList();
+ prev.add(prevLink);
+ feed.setOtherLinks(prev);
+ }
+ feed.setEntries(atomEntries);
+ return feed;
+ }
+ throw new Exception("ERROR: not authorized");
+ }
+
+ //--------------------------------------------------------------------- entries
+
+ /**
+ * Create entry in the entry collection (a Roller blog has only one).
+ */
+ public Entry postEntry(String[] pathInfo, Entry entry) throws Exception {
+ // authenticated client posted a weblog entry
+ String handle = pathInfo[0];
+ WebsiteData website = mRoller.getUserManager().getWebsiteByHandle(handle);
+ if (canEdit(website)) {
+ // Save it and commit it
+ WeblogManager mgr = mRoller.getWeblogManager();
+ WeblogEntryData rollerEntry = createRollerEntry(website, entry);
+ rollerEntry.setCreator(this.user);
+ mgr.saveWeblogEntry(rollerEntry);
+
+ // Throttle one entry per second
+ // (MySQL timestamp has 1 sec resolution, damnit)
+ Thread.sleep(1000);
+
+ CacheManager.invalidate(website);
+ if (rollerEntry.isPublished()) {
+ mRoller.getIndexManager().addEntryReIndexOperation(rollerEntry);
+ }
+ return createAtomEntry(rollerEntry);
+ }
+ throw new Exception("ERROR not authorized to edit website");
+ }
+
+ /**
+ * Retrieve entry, URI like this /blog-name/entry/id
+ */
+ public Entry getEntry(String[] pathInfo) throws Exception {
+ if (pathInfo.length == 3) // URI is /blogname/entries/entryid
+ {
+ WeblogEntryData entry =
+ mRoller.getWeblogManager().getWeblogEntry(pathInfo[2]);
+ if (entry != null && !canView(entry)) {
+ throw new Exception("ERROR not authorized to view entry");
+ } else if (entry != null) {
+ return createAtomEntry(entry);
+ }
+ return null;
+ }
+ throw new Exception("ERROR: bad URI");
+ }
+
+ /**
+ * Update entry, URI like this /blog-name/entry/id
+ */
+ public Entry putEntry(String[] pathInfo, Entry entry) throws Exception {
+ if (pathInfo.length == 3) // URI is /blogname/entries/entryid
+ {
+ WeblogEntryData rollerEntry =
+ mRoller.getWeblogManager().getWeblogEntry(pathInfo[2]);
+ if (canEdit(rollerEntry)) {
+ WeblogManager mgr = mRoller.getWeblogManager();
+
+ WeblogEntryData rawUpdate = createRollerEntry(rollerEntry.getWebsite(), entry);
+ rollerEntry.setPubTime(rawUpdate.getPubTime());
+ rollerEntry.setUpdateTime(rawUpdate.getUpdateTime());
+ rollerEntry.setText(rawUpdate.getText());
+ rollerEntry.setStatus(rawUpdate.getStatus());
+ rollerEntry.setCategory(rawUpdate.getCategory());
+ rollerEntry.setTitle(rawUpdate.getTitle());
+
+ mgr.saveWeblogEntry(rollerEntry);
+
+ CacheManager.invalidate(rollerEntry.getWebsite());
+ if (rollerEntry.isPublished()) {
+ mRoller.getIndexManager().addEntryReIndexOperation(rollerEntry);
+ }
+ return createAtomEntry(rollerEntry);
+ }
+ throw new Exception("ERROR not authorized to put entry");
+ }
+ throw new Exception("ERROR: bad URI");
+ }
+
+ /**
+ * Delete entry, URI like this /blog-name/entry/id
+ */
+ public void deleteEntry(String[] pathInfo) throws Exception {
+ if (pathInfo.length == 3) // URI is /blogname/entries/entryid
+ {
+ WeblogEntryData rollerEntry =
+ mRoller.getWeblogManager().getWeblogEntry(pathInfo[2]);
+ if (canEdit(rollerEntry)) {
+ WeblogManager mgr = mRoller.getWeblogManager();
+ mgr.removeWeblogEntry(rollerEntry);
+ return;
+ }
+ throw new Exception("ERROR not authorized to delete entry");
+ }
+ throw new Exception("ERROR: bad URI");
+ }
+
+ //-------------------------------------------------------------------- resources
+
+ /**
+ * Create new resource in generic collection (a Roller blog has only one).
+ * TODO: can we avoid saving temporary file?
+ * TODO: do we need to handle mutli-part MIME uploads?
+ * TODO: use Jakarta Commons File-upload?
+ */
+ public Entry postMedia(String[] pathInfo,
+ String name, String contentType, InputStream is)
+ throws Exception {
+ // authenticated client posted a weblog entry
+ File tempFile = null;
+ RollerMessages msgs = new RollerMessages();
+ String handle = pathInfo[0];
+ WebsiteData website = mRoller.getUserManager().getWebsiteByHandle(handle);
+ if (canEdit(website) && pathInfo.length > 1) {
+ // save to temp file
+ if (name == null) {
+ throw new Exception("ERROR[postResource]: No 'name' present in HTTP headers");
+ }
+ try {
+ FileManager fmgr = mRoller.getFileManager();
+ tempFile = File.createTempFile(name,"tmp");
+ FileOutputStream fos = new FileOutputStream(tempFile);
+ Utilities.copyInputToOutput(is, fos);
+ fos.close();
+
+ // If save is allowed by Roller system-wide policies
+ if (fmgr.canSave(website.getHandle(), name, tempFile.length(), msgs)) {
+ // Then save the file
+ FileInputStream fis = new FileInputStream(tempFile);
+ fmgr.saveFile(website.getHandle(), name, tempFile.length(), fis);
+ fis.close();
+
+ File resource = new File(fmgr.getUploadDir() + File.separator + name);
+ return createAtomResourceEntry(website, resource);
+ }
+
+ } catch (Exception e) {
+ String msg = "ERROR in atom.postResource";
+ mLogger.error(msg,e);
+ throw new Exception(msg);
+ } finally {
+ if (tempFile != null) tempFile.delete();
+ }
+ }
+ throw new Exception("File upload denied because:" + msgs.toString());
+ }
+
+ /**
+ * Get absolute path to resource specified by path info.
+ */
+ public Entry getMedia(String[] pathInfo) throws Exception {
+ String handle = pathInfo[0];
+ WebsiteData website = mRoller.getUserManager().getWebsiteByHandle(handle);
+ String uploadPath = RollerFactory.getRoller().getFileManager().getUploadUrl();
+ File resource = new File(uploadPath + File.separator + pathInfo[2]);
+ return createAtomResourceEntry(website, resource);
+ }
+
+ /**
+ * Update resource specified by pathInfo using data from input stream.
+ * Expects pathInfo of form /blog-name/resources/name
+ */
+ public Entry putMedia(String[] pathInfo,
+ String contentType, InputStream is) throws Exception {
+ if (pathInfo.length > 2) {
+ String name = pathInfo[2];
+ return postMedia(pathInfo, name, contentType, is);
+ }
+ throw new Exception("ERROR: bad pathInfo");
+ }
+
+ /**
+ * Delete resource specified by pathInfo.
+ * Expects pathInfo of form /blog-name/resources/name
+ */
+ public void deleteMedia(String[] pathInfo) throws Exception {
+ // authenticated client posted a weblog entry
+ String handle = pathInfo[0];
+ WebsiteData website = mRoller.getUserManager().getWebsiteByHandle(handle);
+ if (canEdit(website) && pathInfo.length > 1) {
+ try {
+ FileManager fmgr = mRoller.getFileManager();
+ fmgr.deleteFile(website.getHandle(), pathInfo[2]);
+ } catch (Exception e) {
+ String msg = "ERROR in atom.deleteResource";
+ mLogger.error(msg,e);
+ throw new Exception(msg);
+ }
+ }
+ else throw new Exception("ERROR not authorized to edit website");
+ }
+
+ //------------------------------------------------------------------ URI testers
+
+ /**
+ * True if URL is the introspection URI.
+ */
+ public boolean isIntrospectionURI(String[] pathInfo) {
+ if (pathInfo.length==0) return true;
+ return false;
+ }
+
+ /**
+ * True if URL is a entry URI.
+ */
+ public boolean isEntryURI(String[] pathInfo) {
+ if (pathInfo.length > 1 && pathInfo[1].equals("entry")) return true;
+ return false;
+ }
+
+ /**
+ * True if URL is a resource URI.
+ */
+ public boolean isMediaURI(String[] pathInfo) {
+ if (pathInfo.length > 1 && pathInfo[1].equals("resource")) return true;
+ return false;
+ }
+
+ /**
+ * True if URL is a category URI.
+ */
+ public boolean isCategoryURI(String[] pathInfo) {
+ if (pathInfo.length > 1 && pathInfo[1].equals("category")) return true;
+ return false;
+ }
+
+ /**
+ * True if URL is a collection URI of any sort.
+ */
+ public boolean isCollectionURI(String[] pathInfo) {
+ if (pathInfo.length > 1 && pathInfo[1].equals("entries")) return true;
+ if (pathInfo.length > 1 && pathInfo[1].equals("resources")) return true;
+ if (pathInfo.length > 1 && pathInfo[1].equals("categories")) return true;
+ return false;
+ }
+
+ /**
+ * True if URL is a entry collection URI.
+ */
+ public boolean isEntryCollectionURI(String[] pathInfo) {
+ if (pathInfo.length > 1 && pathInfo[1].equals("entries")) return true;
+ return false;
+ }
+
+ /**
+ * True if URL is a resource collection URI.
+ */
+ public boolean isMediaCollectionURI(String[] pathInfo) {
+ if (pathInfo.length > 1 && pathInfo[1].equals("resources")) return true;
+ return false;
+ }
+
+ /**
+ * True if URL is a category collection URI.
+ */
+ public boolean isCategoryCollectionURI(String[] pathInfo) {
+ if (pathInfo.length > 1 && pathInfo[1].equals("categories")) return true;
+ return false;
+ }
+
+ //------------------------------------------------------------------ permissions
+
+ /**
+ * Return true if user is allowed to edit an entry.
+ */
+ private boolean canEdit(WeblogEntryData entry) {
+ try {
+ return entry.hasWritePermissions(this.user);
+ } catch (Exception e) {
+ mLogger.error("ERROR: checking website.canSave()");
+ }
+ return false;
+ }
+
+ /**
+ * Return true if user is allowed to create/edit weblog entries and file uploads in a website.
+ */
+ private boolean canEdit(WebsiteData website) {
+ try {
+ return website.hasUserPermissions(this.user, PermissionsData.AUTHOR);
+ } catch (Exception e) {
+ mLogger.error("ERROR: checking website.hasUserPermissions()");
+ }
+ return false;
+ }
+
+ /**
+ * Return true if user is allowed to view an entry.
+ */
+ private boolean canView(WeblogEntryData entry) {
+ return canEdit(entry);
+ }
+
+ /**
+ * Return true if user is allowed to view a website.
+ */
+ private boolean canView(WebsiteData website) {
+ return canEdit(website);
+ }
+
+ //-------------------------------------------------------------- authentication
+
+ /**
+ * Perform WSSE authentication based on information in request.
+ * Will not work if Roller password encryption is turned on.
+ */
+ protected String authenticateWSSE(HttpServletRequest request) {
+ String wsseHeader = request.getHeader("X-WSSE");
+ if (wsseHeader == null) return null;
+
+ String ret = null;
+ String userName = null;
+ String created = null;
+ String nonce = null;
+ String passwordDigest = null;
+ String[] tokens = wsseHeader.split(",");
+ for (int i = 0; i < tokens.length; i++) {
+ int index = tokens[i].indexOf('=');
+ if (index != -1) {
+ String key = tokens[i].substring(0, index).trim();
+ String value = tokens[i].substring(index + 1).trim();
+ value = value.replaceAll("\"", "");
+ if (key.startsWith("UsernameToken")) {
+ userName = value;
+ } else if (key.equalsIgnoreCase("nonce")) {
+ nonce = value;
+ } else if (key.equalsIgnoreCase("passworddigest")) {
+ passwordDigest = value;
+ } else if (key.equalsIgnoreCase("created")) {
+ created = value;
+ }
+ }
+ }
+ String digest = null;
+ try {
+ UserData user = mRoller.getUserManager().getUserByUsername(userName);
+ digest = WSSEUtilities.generateDigest(
+ WSSEUtilities.base64Decode(nonce),
+ created.getBytes("UTF-8"),
+ user.getPassword().getBytes("UTF-8"));
+ if (digest.equals(passwordDigest)) {
+ ret = userName;
+ }
+ } catch (Exception e) {
+ mLogger.error("ERROR in wsseAuthenticataion: " + e.getMessage(), e);
+ }
+ return ret;
+ }
+
+ /**
+ * Untested (and currently unused) implementation of BASIC authentication
+ */
+ public String authenticateBASIC(HttpServletRequest request) {
+ boolean valid = false;
+ String userID = null;
+ String password = null;
+ try {
+ String authHeader = request.getHeader("Authorization");
+ if (authHeader != null) {
+ StringTokenizer st = new StringTokenizer(authHeader);
+ if (st.hasMoreTokens()) {
+ String basic = st.nextToken();
+ if (basic.equalsIgnoreCase("Basic")) {
+ String credentials = st.nextToken();
+ String userPass = new String(Base64.decodeBase64(credentials.getBytes()));
+ int p = userPass.indexOf(":");
+ if (p != -1) {
+ userID = userPass.substring(0, p);
+ UserData user = mRoller.getUserManager().getUserByUsername(userID);
+ boolean enabled = user.getEnabled().booleanValue();
+ if (enabled) {
+ // are passwords encrypted?
+ RollerContext rollerContext =
+ RollerContext.getRollerContext();
+ String encrypted =
+ RollerConfig.getProperty("passwds.encryption.enabled");
+ password = userPass.substring(p+1);
+ if ("true".equalsIgnoreCase(encrypted)) {
+ password = Utilities.encodePassword(password,
+ RollerConfig.getProperty("passwds.encryption.algorithm"));
+ }
+ valid = user.getPassword().equals(password);
+ }
+ }
+ }
+ }
+ }
+ } catch (Exception e) {
+ mLogger.debug(e);
+ }
+ if (valid) return userID;
+ return null;
+ }
+
+ //----------------------------------------------------------- internal utilities
+
+ /**
+ * Create a Rome Atom entry based on a Roller entry.
+ * Content is escaped.
+ * Link is stored as rel=alternate link.
+ */
+ private Entry createAtomEntry(WeblogEntryData entry) {
+ Entry atomEntry = new Entry();
+ Content content = new Content();
+ content.setType(Content.HTML);
+ content.setValue(entry.getText());
+ List contents = new ArrayList();
+ contents.add(content);
+
+ String absUrl = mRollerContext.getAbsoluteContextUrl(mRequest);
+ atomEntry.setId( absUrl + entry.getPermaLink());
+ atomEntry.setTitle( entry.getTitle());
+ atomEntry.setContents( contents);
+ atomEntry.setPublished( entry.getPubTime());
+ atomEntry.setUpdated( entry.getUpdateTime());
+
+ UserData creator = entry.getCreator();
+ Person author = new Person();
+ author.setName( creator.getUserName());
+ author.setEmail( creator.getEmailAddress());
+ atomEntry.setAuthors( Collections.singletonList(author));
+
+ List categories = new ArrayList();
+ Category atomCat = new Category();
+ atomCat.setTerm(entry.getCategory().getPath());
+ categories.add(atomCat);
+ 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(absUrl + "/app/"
+ + entry.getWebsite().getHandle() + "/entry/" + entry.getId());
+ List otherlinks = new ArrayList();
+ otherlinks.add(editlink);
+ atomEntry.setOtherLinks(otherlinks);
+
+ List modules = new ArrayList();
+ PubControlModule pubControl = new PubControlModuleImpl();
+ pubControl.setDraft(
+ new Boolean(!WeblogEntryData.PUBLISHED.equals(entry.getStatus())));
+ modules.add(pubControl);
+ atomEntry.setModules(modules);
+
+ return atomEntry;
+ }
+
+ private Entry createAtomResourceEntry(WebsiteData website, File file) {
+ String absUrl = mRollerContext.getAbsoluteContextUrl(mRequest);
+ String editURI = absUrl
+ + "/app/" + website.getHandle()
+ + "/resource/" + file.getName();
+ String viewURI = absUrl
+ + "/resources/" + website.getHandle()
+ + "/" + file.getName();
+ FileTypeMap map = FileTypeMap.getDefaultFileTypeMap();
+ String contentType = map.getContentType(file);
+
+ Entry entry = new Entry();
+ entry.setTitle(file.getName());
+ entry.setUpdated(new Date(file.lastModified()));
+
+ Link editlink = new Link();
+ editlink.setRel("edit");
+ editlink.setHref(editURI);
+ List otherlinks = new ArrayList();
+ otherlinks.add(editlink);
+ entry.setOtherLinks(otherlinks);
+
+ Content content = new Content();
+ content.setSrc(viewURI);
+ content.setType(contentType);
+ List contents = new ArrayList();
+ contents.add(content);
+ entry.setContents(contents);
+
+ return entry;
+ }
+
+ /**
+ * Create a Roller weblog entry based on a Rome Atom entry object
+ */
+ private WeblogEntryData createRollerEntry(WebsiteData website, Entry entry)
+ throws RollerException {
+
+ 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() );
+ }
+ WeblogEntryData rollerEntry = new WeblogEntryData();
+ rollerEntry.setTitle(entry.getTitle());
+ if (entry.getContents() != null && entry.getContents().size() > 0) {
+ Content content = (Content)entry.getContents().get(0);
+ rollerEntry.setText(content.getValue());
+ }
+ rollerEntry.setPubTime(pubTime);
+ rollerEntry.setUpdateTime(updateTime);
+ rollerEntry.setWebsite(website);
+
+ PubControlModule control =
+ (PubControlModule)entry.getModule("http://purl.org/atom/app#");
+ if (control!=null && control.getDraft()!=null && control.getDraft().booleanValue()) {
+ rollerEntry.setStatus(WeblogEntryData.DRAFT);
+ } else {
+ rollerEntry.setStatus(WeblogEntryData.PUBLISHED);
+ }
+
+ // Atom supports multiple cats, Roller 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);
+ WeblogCategoryData rollerCat =
+ mRoller.getWeblogManager().getWeblogCategoryByPath(
+ website, cat.getTerm());
+ if (rollerCat != null) {
+ rollerEntry.setCategory(rollerCat);
+ break;
+ }
+ }
+ } else {
+ rollerEntry.setCategory(website.getBloggerCategory());
+ }
+ return rollerEntry;
+ }
+}
Added: incubator/roller/trunk/src/org/roller/presentation/webservices/atomprotocol/WSSEUtilities.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/presentation/webservices/atomprotocol/WSSEUtilities.java?rev=395633&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/roller/presentation/webservices/atomprotocol/WSSEUtilities.java (added)
+++ incubator/roller/trunk/src/org/roller/presentation/webservices/atomprotocol/WSSEUtilities.java Thu Apr 20 09:22:49 2006
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2005, Dave Johnson
+ *
+ * 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.roller.presentation.webservices.atomprotocol;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import org.apache.commons.codec.binary.Base64;
+
+/**
+ * Utilties to support WSSE authentication.
+ * @author Dave Johnson
+ */
+public class WSSEUtilities {
+ public static synchronized String generateDigest(
+ byte[] nonce, byte[] created, byte[] password) {
+ String result = null;
+ try {
+ MessageDigest digester = MessageDigest.getInstance("SHA");
+ digester.reset();
+ digester.update(nonce);
+ digester.update(created);
+ digester.update(password);
+ byte[] digest = digester.digest();
+ result = new String(base64Encode(digest));
+ }
+ catch (NoSuchAlgorithmException e) {
+ result = null;
+ }
+ return result;
+ }
+ public static byte[] base64Decode(String value) throws IOException {
+ return Base64.decodeBase64(value.getBytes("UTF-8"));
+ }
+ public static String base64Encode(byte[] value) {
+ return new String(Base64.encodeBase64(value));
+ }
+ public static String generateWSSEHeader(String userName, String password)
+ throws UnsupportedEncodingException {
+
+ byte[] nonceBytes = Long.toString(new Date().getTime()).getBytes();
+ String nonce = new String(WSSEUtilities.base64Encode(nonceBytes));
+
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
+ String created = sdf.format(new Date());
+
+ String digest = WSSEUtilities.generateDigest(
+ nonceBytes, created.getBytes("UTF-8"), password.getBytes("UTF-8"));
+
+ StringBuffer header = new StringBuffer("UsernameToken Username=\"");
+ header.append(userName);
+ header.append("\", ");
+ header.append("PasswordDigest=\"");
+ header.append(digest);
+ header.append("\", ");
+ header.append("Nonce=\"");
+ header.append(nonce);
+ header.append("\", ");
+ header.append("Created=\"");
+ header.append(created);
+ header.append("\"");
+ return header.toString();
+ }
+}
Added: incubator/roller/trunk/src/org/roller/presentation/webservices/atomprotocol/package.html
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/presentation/webservices/atomprotocol/package.html?rev=395633&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/roller/presentation/webservices/atomprotocol/package.html (added)
+++ incubator/roller/trunk/src/org/roller/presentation/webservices/atomprotocol/package.html Thu Apr 20 09:22:49 2006
@@ -0,0 +1,10 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+ <title></title>
+</head>
+<body>
+ROME-based Atom Protocol implementation.
+
+</body>
+</html>