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 2005/10/21 23:46:28 UTC
svn commit: r327589 [29/72] - in /incubator/roller/branches/roller_1.x: ./
contrib/ contrib/lib/ contrib/plugins/ contrib/plugins/src/
contrib/plugins/src/org/ contrib/plugins/src/org/roller/
contrib/plugins/src/org/roller/presentation/ contrib/plugins...
Added: incubator/roller/branches/roller_1.x/src/org/roller/presentation/RollerRequest.java
URL: http://svn.apache.org/viewcvs/incubator/roller/branches/roller_1.x/src/org/roller/presentation/RollerRequest.java?rev=327589&view=auto
==============================================================================
--- incubator/roller/branches/roller_1.x/src/org/roller/presentation/RollerRequest.java (added)
+++ incubator/roller/branches/roller_1.x/src/org/roller/presentation/RollerRequest.java Fri Oct 21 14:27:36 2005
@@ -0,0 +1,934 @@
+
+package org.roller.presentation;
+
+import java.text.ParsePosition;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletRequest;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.jsp.PageContext;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.roller.RollerException;
+import org.roller.config.RollerRuntimeConfig;
+import org.roller.model.ParsedRequest;
+import org.roller.model.Roller;
+import org.roller.model.RollerFactory;
+import org.roller.pojos.Template;
+import org.roller.model.UserManager;
+import org.roller.model.WeblogManager;
+import org.roller.pojos.BookmarkData;
+import org.roller.pojos.FolderData;
+import org.roller.pojos.UserData;
+import org.roller.pojos.WeblogCategoryData;
+import org.roller.pojos.WeblogEntryData;
+import org.roller.pojos.WebsiteData;
+import org.roller.util.DateUtil;
+import org.roller.util.Utilities;
+
+//////////////////////////////////////////////////////////////////////////////
+/**
+ * Access to objects and values specified by request. Parses out arguments from
+ * request URL needed for various parts of Roller and makes them available via
+ * getter methods.
+ * <br/><br/>
+ *
+ * These forms of pathinfo get special support:
+ * <br/><br/>
+ *
+ * <pre>
+ * [username] - get default page for user for today's date
+ * [username]/[date] - get default page for user for specified date
+ * [username]/[pagelink] - get specified page for today's date
+ * [username]/[pagelink]/[date] - get specified page for specified date
+ * [username]/[pagelink]/[anchor] - get specified page & entry (by anchor)
+ * [username]/[pagelink]/[date]/[anchor] - get specified page & entry (by anchor)
+ * </pre>
+ *
+ * @author David M Johnson
+ */
+public class RollerRequest implements ParsedRequest
+{
+ //----------------------------------------------------------------- Fields
+
+ private static Log mLogger =
+ LogFactory.getFactory().getInstance(RollerRequest.class);
+
+ private BookmarkData mBookmark;
+ private ServletContext mContext = null;
+ private Date mDate = null;
+ private String mDateString = null;
+ private String mPathInfo = null;
+ private String mPageLink = null;
+ private Template mPage;
+ private PageContext mPageContext = null;
+ private HttpServletRequest mRequest = null;
+ private WebsiteData mWebsite;
+ private WeblogEntryData mWeblogEntry;
+ private WeblogCategoryData mWeblogCategory;
+ private boolean mIsDateSpecified = false;
+
+ private static ThreadLocal mRollerRequestTLS = new ThreadLocal();
+
+ public static final String ANCHOR_KEY = "entry";
+ public static final String ANCHOR_KEY_OLD = "anchor";
+ public static final String USERNAME_KEY = "username";
+ public static final String WEBSITEID_KEY = "websiteid";
+ public static final String FOLDERID_KEY = "folderid";
+ public static final String PARENTID_KEY = "parentid";
+ public static final String NEWSFEEDID_KEY = "feedid";
+ public static final String PAGEID_KEY = "pageid";
+ public static final String PAGELINK_KEY = "pagelink";
+ public static final String PINGTARGETID_KEY = "pingtargetid";
+ public static final String EXCERPTS_KEY = "excerpts";
+ public static final String BOOKMARKID_KEY = "bookmarkid";
+ public static final String REFERERID_KEY = "refid";
+ public static final String WEBLOGENTRYID_KEY = "entryid";
+ public static final String WEBLOGENTRY_COUNT = "count";
+ public static final String WEBLOGCATEGORYNAME_KEY = "catname";
+ public static final String WEBLOGCATEGORYID_KEY = "catid";
+ public static final String WEBLOGENTRIES_KEY = "entries";
+ public static final String WEBLOGDAY_KEY = "day";
+ public static final String WEBLOGCOMMENTID_KEY = "catid";
+ public static final String LOGIN_COOKIE = "sessionId";
+
+ public static final String OWNING_USER = "OWNING_USER";
+
+ private static final String ROLLER_REQUEST = "roller_request";
+
+ private SimpleDateFormat mFmt = DateUtil.get8charDateFormat();
+
+ //----------------------------------------------------------- Constructors
+
+ /** Construct Roller request for a Servlet request */
+ public RollerRequest( HttpServletRequest req, ServletContext ctx )
+ throws RollerException
+ {
+ mRequest = req;
+ mContext = ctx;
+ init();
+ }
+
+ //------------------------------------------------------------------------
+ /** Convenience */
+ public RollerRequest( ServletRequest req, ServletContext ctx )
+ throws RollerException
+ {
+ mRequest = (HttpServletRequest)req;
+ mContext = ctx;
+ init();
+ }
+
+ //------------------------------------------------------------------------
+ public RollerRequest( PageContext pCtx) throws RollerException
+ {
+ mRequest = (HttpServletRequest) pCtx.getRequest();
+ mContext = pCtx.getServletContext();
+ mPageContext = pCtx;
+ init();
+ }
+
+ //------------------------------------------------------------------------
+ private void init() throws RollerException
+ {
+ mRollerRequestTLS.set(this);
+ if (mRequest.getContextPath().equals("/atom"))
+ {
+ return; // Atom servlet request needs no init
+ }
+
+ // Bind persistence session to authenticated user, if we have one
+ RollerContext rctx = RollerContext.getRollerContext(mContext);
+ Authenticator auth = rctx.getAuthenticator();
+ String userName = auth.getAuthenticatedUserName(mRequest);
+ if (userName != null)
+ {
+ UserManager userMgr = getRoller().getUserManager();
+ UserData currentUser = userMgr.getUser(userName);
+ getRoller().setUser(currentUser);
+ }
+
+ // path info may be null, (e.g. on JSP error page)
+ mPathInfo = mRequest.getPathInfo();
+ mPathInfo = (mPathInfo!=null) ? mPathInfo : "";
+
+ String[] pathInfo = StringUtils.split(mPathInfo,"/");
+ if ( pathInfo.length > 0 )
+ {
+ // Parse pathInfo and throw exception if it is invalid
+ if (!"j_security_check".equals(pathInfo[0]))
+ {
+ parsePathInfo( pathInfo );
+ return;
+ }
+ }
+
+ // Parse user, page, and entry from request params if possible
+ parseRequestParams();
+ }
+
+ //------------------------------------------------------------------------
+ /**
+ * Bind persistence session to specific user.
+ */
+ private void bindUser() throws RollerException
+ {
+ }
+
+ //------------------------------------------------------------------------
+ /** Parse pathInfo and throw exception if it is invalid */
+ private void parsePathInfo( String[] pathInfo ) throws RollerException
+ {
+ try
+ {
+ // If there is any path info, it must be in one of the
+ // below formats or the request is considered to be invalid.
+ //
+ // /username
+ // /username/datestring
+ // /username/pagelink
+ // /username/pagelink/datestring
+ // /username/pagelink/anchor (specific entry)
+ // /username/pagelink/datestring/anchor (specific entry)
+ UserManager userMgr = getRoller().getUserManager();
+ mWebsite = userMgr.getWebsite(pathInfo[0]);
+ if (mWebsite != null)
+ {
+ if ( pathInfo.length == 1 )
+ {
+ // we have the /username form of URL
+ mDate = getDate(true);
+ mDateString = DateUtil.format8chars(mDate);
+ mPage = mWebsite.getDefaultPage();
+ }
+ else if ( pathInfo.length == 2 )
+ {
+ mDate = parseDate(pathInfo[1]);
+ if ( mDate == null ) // pre-jdk1.4 --> || mDate.getYear() <= 70 )
+ {
+ // we have the /username/pagelink form of URL
+ mDate = getDate(true);
+ mDateString = DateUtil.format8chars(mDate);
+ mPageLink = pathInfo[1];
+ mPage = mWebsite.getPageByLink(pathInfo[1]);
+ }
+ else
+ {
+ // we have the /username/datestring form of URL
+ mDateString = pathInfo[1];
+ mPage = mWebsite.getDefaultPage();
+ mIsDateSpecified = true;
+ }
+ }
+ else if ( pathInfo.length == 3 )
+ {
+ mPageLink = pathInfo[1];
+ mPage = mWebsite.getPageByLink(pathInfo[1]);
+
+ mDate = parseDate(pathInfo[2]);
+ if ( mDate == null ) // pre-jdk1.4 --> || mDate.getYear() <= 70 )
+ {
+ // we have the /username/pagelink/anchor form of URL
+ try
+ {
+ WeblogManager weblogMgr = getRoller().getWeblogManager();
+ mWeblogEntry = weblogMgr.getWeblogEntryByAnchor(
+ mWebsite, pathInfo[2]);
+ mDate = mWeblogEntry.getPubTime();
+ mDateString = DateUtil.format8chars(mDate);
+ }
+ catch (Exception damn)
+ {
+ // doesn't really matter, we've got the Page anyway
+ mLogger.debug("Damn", damn);
+ }
+ }
+ else
+ {
+ // we have the /username/pagelink/datestring form of URL
+ mDateString = pathInfo[2];
+ mIsDateSpecified = true;
+ }
+ }
+ else if ( pathInfo.length == 4 )
+ {
+ // we have the /username/pagelink/datestring/anchor form of URL
+ mPageLink = pathInfo[1];
+ mPage = mWebsite.getPageByLink(pathInfo[1]);
+
+ mDate = parseDate(pathInfo[2]);
+ mDateString = pathInfo[2];
+ mIsDateSpecified = true;
+
+ // we have the /username/pagelink/date/anchor form of URL
+ WeblogManager weblogMgr = getRoller().getWeblogManager();
+ mWeblogEntry = weblogMgr.getWeblogEntryByAnchor(
+ mWebsite, pathInfo[3]);
+ }
+ }
+ }
+ catch ( Exception ignored )
+ {
+ mLogger.debug("Exception parsing pathInfo",ignored);
+ }
+
+ if ( mWebsite==null || mDate==null || mPage==null )
+ {
+ String msg = "Invalid pathInfo: "+StringUtils.join(pathInfo,"|");
+ mLogger.info(msg);
+ throw new RollerException(msg);
+ }
+ }
+
+ //------------------------------------------------------------------------
+ /** Parse user, page, and entry from request params if possible */
+ private void parseRequestParams()
+ {
+ try
+ {
+ // No path info means that we need to parse request params
+
+ // First, look for user in the request params
+ UserManager userMgr = getRoller().getUserManager();
+ String userName = mRequest.getParameter(USERNAME_KEY);
+ if ( userName == null )
+ {
+ // then try remote user
+ userName = mRequest.getRemoteUser();
+ }
+
+ if ( userName != null )
+ {
+ mWebsite = userMgr.getWebsite(userName);
+ }
+
+ // Look for page ID in request params
+ String pageId = mRequest.getParameter(RollerRequest.PAGEID_KEY);
+ if ( pageId != null )
+ {
+ mPage = userMgr.retrievePage(pageId);
+ /*
+ // We can use page to find the user, if we don't have one yet
+ if ( mWebsite == null )
+ {
+ mWebsite = mPage.getWebsite();
+ }
+ */
+ }
+ else if (mWebsite != null)
+ {
+ mPage = mWebsite.getDefaultPage();
+ }
+
+ // Look for day in request params
+ String daystr = mRequest.getParameter(WEBLOGDAY_KEY);
+ if ( daystr != null )
+ {
+ mDate = parseDate(daystr);
+ if ( mDate != null )
+ {
+ // we have the /username/datestring form of URL
+ mDateString = daystr;
+ mIsDateSpecified = true;
+ }
+ }
+ }
+ catch ( Exception ignored )
+ {
+ mLogger.debug("Exception parsing request params",ignored);
+ }
+ }
+
+ //------------------------------------------------------------------------
+ /** Get HttpServletmRequest that is wrapped by this RollerRequest */
+ public PageContext getPageContext()
+ {
+ return mPageContext;
+ }
+
+ public void setPageContext(PageContext p)
+ {
+ mPageContext = p;
+ }
+
+ //------------------------------------------------------------------------
+ /** Get HttpServletmRequest that is wrapped by this RollerRequest */
+ public HttpServletRequest getRequest()
+ {
+ return mRequest;
+ }
+
+ //------------------------------------------------------------------------
+ /**
+ * Get the RollerRequest object that is stored in the request. Creates
+ * RollerRequest if one not found in mRequest.
+ */
+ public static RollerRequest getRollerRequest(
+ HttpServletRequest r, ServletContext ctx ) throws RollerException
+ {
+ RollerRequest ret= (RollerRequest)r.getAttribute(ROLLER_REQUEST);
+ if ( ret == null )
+ {
+ ret = new RollerRequest(r,ctx);
+ r.setAttribute( ROLLER_REQUEST, ret );
+ }
+ return ret;
+ }
+
+ //------------------------------------------------------------------------
+ /**
+ * Get the RollerRequest object that is stored in the request. Creates
+ * RollerRequest if one not found in mRequest.
+ */
+ public static RollerRequest getRollerRequest( HttpServletRequest r )
+ {
+ try
+ {
+ return getRollerRequest(r, RollerContext.getServletContext());
+ }
+ catch (Exception e)
+ {
+ mLogger.debug("Unable to create a RollerRequest", e);
+ }
+ return null;
+ }
+
+ //------------------------------------------------------------------------
+ /**
+ * Get the RollerRequest object that is stored in the request. Creates
+ * RollerRequest if one not found in mRequest.
+ */
+ public static RollerRequest getRollerRequest( PageContext p )
+ {
+ HttpServletRequest r = (HttpServletRequest)p.getRequest();
+ RollerRequest ret = (RollerRequest)r.getAttribute(ROLLER_REQUEST);
+ if (ret == null)
+ {
+ try
+ {
+ ret = new RollerRequest( p );
+ r.setAttribute( ROLLER_REQUEST, ret );
+ }
+ catch (Exception e)
+ {
+ mLogger.debug("Unable to create a RollerRequest", e);
+ }
+ }
+ else
+ {
+ ret.setPageContext( p );
+ }
+ return ret;
+ }
+
+ //------------------------------------------------------------------------
+ /**
+ * Get RollerRequest object for the current thread using EVIL MAGIC,
+ * do not use unless you absolutely, positively, cannot use on of the
+ * getRollerRequest() methods.
+ */
+ public static RollerRequest getRollerRequest()
+ {
+ return (RollerRequest)mRollerRequestTLS.get();
+ }
+
+ //------------------------------------------------------------------------
+ /**
+ * Get the RollerRequest object that is stored in the requeset.
+ * Creates RollerRequest if one not found in mRequest.
+ */
+ public ServletContext getServletContext()
+ {
+ return mContext;
+ }
+
+
+ public Roller getRoller()
+ {
+ return RollerFactory.getRoller();
+ }
+
+
+ //------------------------------------------------------------------------
+ /** Is mRequest's user the admin user? */
+ public boolean isAdminUser() throws RollerException
+ {
+ UserData user = getUser();
+ if (user != null && user.hasRole("admin"))
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ //------------------------------------------------------------------------
+ /** Is mRequest's user authorized to edit the mRequested resource */
+ public boolean isUserAuthorizedToEdit() throws RollerException
+ {
+ // Make sure authenticated user is the owner of this item
+ // Session's principal's name must match user name in mRequest
+
+ RollerContext rctx = RollerContext.getRollerContext(mContext);
+ Authenticator auth = rctx.getAuthenticator();
+
+ String userName = auth.getAuthenticatedUserName(mRequest);
+
+ // TODO: A hack to be replaced by Object.canEdit()
+ UserData owningUser = null;
+ if (mRequest.getAttribute(OWNING_USER) != null)
+ {
+ owningUser = (UserData)mRequest.getAttribute(OWNING_USER);
+ }
+ else
+ {
+ owningUser = getUser();
+ }
+
+ if ( userName != null
+ && owningUser != null
+ && userName.equalsIgnoreCase( owningUser.getUserName() )
+ && auth.isAuthenticatedUserInRole(mRequest,"editor"))
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ //------------------------------------------------------------------------
+ /**
+ * Get user by name.
+ */
+ public UserData getUser( String userName ) throws Exception
+ {
+ return getRoller().getUserManager().getUser(userName);
+ }
+
+ //------------------------------------------------------------------------
+
+ public boolean isDateSpecified()
+ {
+ return mIsDateSpecified;
+ }
+
+ //------------------------------------------------------------------------
+
+ public int getWeblogEntryCount()
+ {
+ // Get count of entries to return, or 20 if null
+ int count = 20;
+ if ( mRequest.getParameter("count") != null )
+ {
+ count= Utilities.stringToInt(mRequest.getParameter("count"));
+ if ( count==0 || count>50 )
+ {
+ count = 20;
+ }
+ }
+ return count;
+ }
+
+ //------------------------------------------------------------------------
+
+ /**
+ * Gets the date specified by the request, or null.
+ * @return Date
+ */
+ public Date getDate()
+ {
+ return getDate(false);
+ }
+
+ //------------------------------------------------------------------------
+ /**
+ * Gets the date specified by the request
+ * @param orToday If no date specified, then use today's date.
+ * @return Date
+ */
+ public Date getDate( boolean orToday )
+ {
+ Date ret = null;
+ if ( mDate != null )
+ {
+ ret = mDate;
+ }
+ else if ( orToday )
+ {
+ ret = getToday();
+ }
+ return ret;
+ }
+
+ /**
+ * Gets the current date based on Website's Locale & Timezone.
+ * @return
+ */
+ private Date getToday()
+ {
+ Calendar todayCal = Calendar.getInstance();
+ if (this.getWebsite() != null)
+ {
+ todayCal = Calendar.getInstance(
+ this.getWebsite().getTimeZoneInstance(),
+ this.getWebsite().getLocaleInstance());
+ }
+ todayCal.setTime( new Date() );
+ return todayCal.getTime();
+ }
+
+ //------------------------------------------------------------------------
+ /**
+ * Gets the YYYYMMDD date string specified by the request, or null.
+ * @return String
+ */
+ public String getDateString()
+ {
+ return getDateString(false);
+ }
+
+ //------------------------------------------------------------------------
+ /**
+ * Gets the date specified by the request
+ * @param orToday If no date specified, then use today's date.
+ * @return Date
+ */
+ public String getDateString( boolean orToday )
+ {
+ String ret = null;
+ if ( mDateString != null )
+ {
+ ret = mDateString;
+ }
+ else if ( orToday )
+ {
+ Calendar todayCal = Calendar.getInstance();
+ if (this.getWebsite() != null)
+ {
+ todayCal = Calendar.getInstance(
+ this.getWebsite().getTimeZoneInstance(),
+ this.getWebsite().getLocaleInstance());
+ }
+ todayCal.setTime( new Date() );
+ ret = mFmt.format(todayCal.getTime());
+ }
+ return ret;
+ }
+
+ //------------------------------------------------------------------------
+ /**
+ * Gets the path-info specified by the request, or null.
+ * @return String
+ */
+ public String getPathInfo()
+ {
+ return mPathInfo;
+ }
+
+ //------------------------------------------------------------------------
+ /**
+ * Gets the page link name specified by the request, or null.
+ * @return String
+ */
+ public String getPageLink()
+ {
+ return mPageLink;
+ }
+
+ //------------------------------------------------------------------------
+ /**
+ * Gets the BookmarkData specified by the request, or null.
+ * @return BookmarkData
+ */
+ public BookmarkData getBookmark( )
+ {
+ if ( mBookmark == null )
+ {
+ String id = getFromRequest(BOOKMARKID_KEY);
+ if ( id != null )
+ {
+ try
+ {
+ mBookmark =
+ getRoller().getBookmarkManager().retrieveBookmark(id);
+ }
+ catch (RollerException e)
+ {
+ mLogger.error("Getting bookmark from request",e);
+ }
+ }
+ }
+ return mBookmark;
+ }
+
+ //------------------------------------------------------------------------
+ /**
+ * Gets the WeblogCategoryData specified by the request, or null.
+ * @return
+ */
+ public WeblogCategoryData getWeblogCategory()
+ {
+ if ( mWeblogCategory == null )
+ {
+ String id = getFromRequest(WEBLOGCATEGORYID_KEY);
+ if ( id != null )
+ {
+ try
+ {
+ mWeblogCategory =
+ getRoller().getWeblogManager().retrieveWeblogCategory(id);
+ }
+ catch (RollerException e)
+ {
+ mLogger.error("Getting weblog category by id from request",e);
+ }
+ }
+ else if (StringUtils.isNotEmpty(id = getFromRequest(WEBLOGCATEGORYNAME_KEY)))
+ {
+ try
+ {
+ mWeblogCategory =
+ getRoller().getWeblogManager().getWeblogCategoryByPath(
+ getWebsite(), null, id);
+ }
+ catch (RollerException e)
+ {
+ mLogger.error("Getting weblog category by name from request",e);
+ }
+ }
+ }
+ return mWeblogCategory;
+ }
+
+ //------------------------------------------------------------------------
+ /**
+ * Gets the FolderData specified by the request, or null.
+ * @return FolderData
+ */
+ public FolderData getFolder( )
+ {
+ FolderData folder = null;
+ //if ( folder == null )
+ //{
+ String id = getFromRequest(FOLDERID_KEY);
+ if ( id != null )
+ {
+ try
+ {
+ folder =
+ getRoller().getBookmarkManager().retrieveFolder(id);
+ }
+ catch (RollerException e)
+ {
+ mLogger.error("Getting folder from request",e);
+ }
+ }
+ //}
+ return folder;
+ }
+
+ //------------------------------------------------------------------------
+ /**
+ * Gets the WeblogTemplate specified by the request, or null.
+ * @return WeblogTemplate
+ */
+ public Template getPage()
+ {
+ if (mPage == null)
+ {
+ String id = getFromRequest(PAGEID_KEY);
+ if ( id != null )
+ {
+ try
+ {
+ mPage = getRoller().getUserManager().retrievePage(id);
+ }
+ catch (RollerException e)
+ {
+ mLogger.error("Getting page from request",e);
+ }
+ }
+ }
+ return mPage;
+ }
+
+ /**
+ * Allow comment servlet to inject page that it has chosen.
+ */
+ public void setPage(org.roller.pojos.Template page)
+ {
+ mPage = page;
+ }
+
+ //------------------------------------------------------------------------
+ /**
+ * Gets the Request URL specified by the request, or null.
+ * @return String
+ */
+ public String getRequestURL( )
+ {
+ return mRequest.getRequestURL().toString();
+ }
+
+ //------------------------------------------------------------------------
+
+ /**
+ * Gets the Referer URL specified by the request, or null.
+ * @return String
+ */
+ public String getRefererURL( )
+ {
+ return mRequest.getHeader("referer");
+ }
+
+ /**
+ * Gets the UserData specified by the request, or null.
+ * @return UserData
+ */
+ public UserData getUser()
+ {
+ if (mWebsite != null) return mWebsite.getUser();
+ return null;
+ }
+
+ /**
+ * Gets the WebsiteData specified by the request, or null.
+ * @return WeblogCategory
+ */
+ public WebsiteData getWebsite()
+ {
+ return mWebsite;
+ }
+ public void setWebsite(WebsiteData wd)
+ {
+ mWebsite = wd;
+ }
+
+ /**
+ * Gets the WeblogEntryData specified by the request, or null.
+ *
+ * Why is this done lazily in the parseRequestParameters() method?
+ *
+ * Because: that method is called from init(), which is called from
+ * a ServletFilter, and sometimes request parameters are not available
+ * in a ServletFiler. They ARE available when the URL points to a JSP,
+ * but they ARE NOT available when the URL points to the PageServlet.
+ * This may be a Tomcat bug, I'm not sure.
+ *
+ * @return WeblogEntryData
+ */
+ public WeblogEntryData getWeblogEntry( )
+ {
+ if ( mWeblogEntry == null )
+ {
+ // Look for anchor or entry ID that identifies a specific entry
+ String anchor = mRequest.getParameter(ANCHOR_KEY);
+ if (anchor == null) anchor = mRequest.getParameter(ANCHOR_KEY_OLD);
+ String entryid = mRequest.getParameter(WEBLOGENTRYID_KEY);
+ if (entryid == null)
+ {
+ entryid = (String)mRequest.getAttribute(WEBLOGENTRYID_KEY);
+ }
+ try
+ {
+ if ( entryid != null )
+ {
+ WeblogManager weblogMgr = getRoller().getWeblogManager();
+ mWeblogEntry = weblogMgr.retrieveWeblogEntry(entryid);
+
+ // We can use entry to find the website, if we don't have one
+ if ( mWeblogEntry != null && mWebsite == null )
+ {
+ mWebsite = mWeblogEntry.getWebsite();
+ }
+ }
+ else if ( mWebsite != null && anchor != null )
+ {
+ WeblogManager weblogMgr = getRoller().getWeblogManager();
+ mWeblogEntry = weblogMgr.getWeblogEntryByAnchor(
+ mWebsite,anchor);
+ }
+ }
+ catch (RollerException e)
+ {
+ mLogger.error("EXCEPTION getting weblog entry",e);
+ mLogger.error("user=" + mWebsite.getUser());
+ mLogger.error("anchor=" + anchor);
+ mLogger.error("entryid=" + entryid);
+ }
+ }
+ return mWeblogEntry;
+ }
+
+ //------------------------------------------------------------------------
+
+ /** Get attribute from mRequest, and if that fails try session */
+ private String getFromRequest( String key )
+ {
+ String ret = (String)mRequest.getAttribute( key );
+ if ( ret == null )
+ {
+ ret = mRequest.getParameter( key );
+ if (ret == null && mRequest.getSession(false) != null)
+ {
+ ret = (String)mRequest.getSession().getAttribute( key );
+ }
+ }
+ return ret;
+ }
+
+ //------------------------------------------------------------------------
+
+ private Date parseDate( String dateString )
+ {
+ Date ret = null;
+ if ( dateString!=null
+ && dateString.length()==8
+ && StringUtils.isNumeric(dateString) )
+ {
+ ParsePosition pos = new ParsePosition(0);
+ ret = mFmt.parse( dateString, pos );
+
+ // make sure the requested date is not in the future
+ Date today = getToday();
+ if (ret.after(today)) ret = today;
+
+ // since a specific date was requested set time to end of day
+ ret = DateUtil.getEndOfDay(ret);
+ }
+ return ret;
+ }
+
+ //------------------------------------------------------------------------
+
+ public String toString()
+ {
+ StringBuffer sb = new StringBuffer();
+ sb.append("[");
+ sb.append(getRequestURL());
+ sb.append(", ");
+ sb.append(getRefererURL());
+ sb.append("]");
+ return sb.toString();
+ }
+
+ //------------------------------------------------------------------------
+
+ /**
+ * @see org.roller.pojos.ParsedRequest#isLinkbackEnabled()
+ */
+ public boolean isEnableLinkback()
+ {
+ return RollerRuntimeConfig.getBooleanProperty("site.linkbacks.enabled");
+ }
+}
+
Added: incubator/roller/branches/roller_1.x/src/org/roller/presentation/RollerSession.java
URL: http://svn.apache.org/viewcvs/incubator/roller/branches/roller_1.x/src/org/roller/presentation/RollerSession.java?rev=327589&view=auto
==============================================================================
--- incubator/roller/branches/roller_1.x/src/org/roller/presentation/RollerSession.java (added)
+++ incubator/roller/branches/roller_1.x/src/org/roller/presentation/RollerSession.java Fri Oct 21 14:27:36 2005
@@ -0,0 +1,146 @@
+package org.roller.presentation;
+
+import org.apache.commons.collections.ArrayStack;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+import javax.servlet.http.HttpSessionActivationListener;
+import javax.servlet.http.HttpSessionEvent;
+import javax.servlet.http.HttpSessionListener;
+import java.io.Serializable;
+
+
+//////////////////////////////////////////////////////////////////////////////
+/**
+ * Roller session handles session startup and shutdown.
+ * @web.listener
+ */
+public class RollerSession
+ implements HttpSessionListener, HttpSessionActivationListener, Serializable
+{
+ // Although we have no actual members, we implement Serializable to meet expectations of
+ // session attributes for some container configurations.
+ static final long serialVersionUID = 5890132909166913727L;
+
+ private static Log mLogger =
+ LogFactory.getFactory().getInstance(RollerSession.class);
+
+ public static final String ROLLER_SESSION = "org.roller.rollersession";
+ public static final String BREADCRUMB = "org.roller.breadcrumb";
+ public static final String ERROR_MESSAGE = "rollererror_message";
+ public static final String STATUS_MESSAGE = "rollerstatus_message";
+
+
+ //------------------------------------------------------------------------
+ /** Create session's Roller instance */
+ public void sessionCreated(HttpSessionEvent se)
+ {
+ // put this in session, so that we get HttpSessionActivationListener callbacks
+ se.getSession().setAttribute( ROLLER_SESSION, this );
+
+ RollerContext rctx = RollerContext.getRollerContext(
+ se.getSession().getServletContext());
+ rctx.sessionCreated(se);
+ }
+
+ //------------------------------------------------------------------------
+ public void sessionDestroyed(HttpSessionEvent se)
+ {
+ RollerContext rctx = RollerContext.getRollerContext(
+ se.getSession().getServletContext());
+ rctx.sessionDestroyed(se);
+
+ clearSession(se);
+ }
+
+ //------------------------------------------------------------------------
+ /** Init session as if it was new */
+ public void sessionDidActivate(HttpSessionEvent se)
+ {
+ }
+
+ //------------------------------------------------------------------------
+ /**
+ * Clear bread crumb trail.
+ * @param req the request
+ */
+ public static void clearBreadCrumbTrail( HttpServletRequest req )
+ {
+ HttpSession ses = req.getSession(false);
+ if (ses != null && ses.getAttribute(BREADCRUMB) != null)
+ {
+ ArrayStack stack = (ArrayStack)ses.getAttribute(BREADCRUMB);
+ stack.clear();
+ }
+ }
+
+ //------------------------------------------------------------------------
+ /**
+ * Store the url of the latest request stored in the session.
+ * @param useReferer If true try to return the "referer" header.
+ */
+ public static String getBreadCrumb(
+ HttpServletRequest req, boolean useReferer )
+ {
+ String crumb = null;
+
+ HttpSession ses = req.getSession(false);
+ if (ses != null && ses.getAttribute(BREADCRUMB) != null)
+ {
+ ArrayStack stack = (ArrayStack) ses.getAttribute(BREADCRUMB);
+ if (stack != null && !stack.empty())
+ {
+ crumb = (String)stack.peek();
+ }
+ }
+
+ if ( crumb == null && useReferer )
+ {
+ crumb = req.getHeader("referer");
+ }
+
+ return crumb;
+ }
+
+ //------------------------------------------------------------------------
+ /**
+ * Store the url of the latest request stored in the session.
+ * Else try to return the "referer" header.
+ */
+ public static String getBreadCrumb( HttpServletRequest req )
+ {
+ return getBreadCrumb(req,true);
+ }
+
+ //------------------------------------------------------------------------
+ /** Purge session before passivation. Because Roller currently does not
+ * support session recovery, failover, migration, or whatever you want
+ * to call it when sessions are saved and then restored at some later
+ * point in time.
+ */
+ public void sessionWillPassivate(HttpSessionEvent se)
+ {
+ clearSession(se);
+ }
+
+ //------------------------------------------------------------------------ /*
+ private void clearSession( HttpSessionEvent se )
+ {
+ HttpSession session = se.getSession();
+ try
+ {
+ session.removeAttribute( BREADCRUMB );
+ }
+ catch (Throwable e)
+ {
+ if (mLogger.isDebugEnabled())
+ {
+ // ignore purge exceptions
+ mLogger.debug("EXCEPTION PURGING session attributes",e);
+ }
+ }
+ }
+}
+
Added: incubator/roller/branches/roller_1.x/src/org/roller/presentation/TurnoverReferersTask.java
URL: http://svn.apache.org/viewcvs/incubator/roller/branches/roller_1.x/src/org/roller/presentation/TurnoverReferersTask.java?rev=327589&view=auto
==============================================================================
--- incubator/roller/branches/roller_1.x/src/org/roller/presentation/TurnoverReferersTask.java (added)
+++ incubator/roller/branches/roller_1.x/src/org/roller/presentation/TurnoverReferersTask.java Fri Oct 21 14:27:36 2005
@@ -0,0 +1,44 @@
+/*
+ * Created on Aug 16, 2003
+ */
+package org.roller.presentation;
+
+import java.util.TimerTask;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.roller.RollerException;
+import org.roller.model.RefererManager;
+import org.roller.model.Roller;
+import org.roller.model.RollerFactory;
+import org.roller.model.ScheduledTask;
+
+/**
+ * @author aim4min
+ */
+public class TurnoverReferersTask extends TimerTask implements ScheduledTask
+{
+ private static Log mLogger = LogFactory.getFactory().getInstance(
+ TurnoverReferersTask.class);
+ private RefererManager refManager = null;
+
+ public void init(Roller roller, String realPath) throws RollerException
+ {
+ refManager = roller.getRefererManager();
+ }
+ public void run()
+ {
+ if (refManager != null)
+ try
+ {
+ RollerFactory.getRoller().begin();
+ refManager.checkForTurnover(false, null);
+ RollerFactory.getRoller().commit();
+ RollerFactory.getRoller().release();
+ }
+ catch (RollerException e)
+ {
+ mLogger.error("Error while checking for referer turnover", e);
+ }
+ }
+}
\ No newline at end of file
Added: incubator/roller/branches/roller_1.x/src/org/roller/presentation/atomapi/AtomCollection.java
URL: http://svn.apache.org/viewcvs/incubator/roller/branches/roller_1.x/src/org/roller/presentation/atomapi/AtomCollection.java?rev=327589&view=auto
==============================================================================
--- incubator/roller/branches/roller_1.x/src/org/roller/presentation/atomapi/AtomCollection.java (added)
+++ incubator/roller/branches/roller_1.x/src/org/roller/presentation/atomapi/AtomCollection.java Fri Oct 21 14:27:36 2005
@@ -0,0 +1,262 @@
+/*
+ * 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.atomapi;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+
+import org.jdom.Document;
+import org.jdom.Element;
+import org.jdom.Namespace;
+
+/**
+ * Models an Atom collection.
+ *
+ * @author Dave Johnson
+ */
+/*
+ * Based on: draft-ietf-atompub-protocol-04.txt
+ *
+ * appCollection = element
+ * app:collection {
+ * attribute next { text } ?,
+ * appMember*
+ * }
+ *
+ * Here is an example Atom collection:
+ *
+ * <?xml version="1.0" encoding='utf-8'?>
+ * <collection xmlns="http://purl.org/atom/app#">
+ * <member href="http://example.org/1"
+ * hrefreadonly="http://example.com/1/bar"
+ * title="Sample 1"
+ * updated="2003-12-13T18:30:02Z" />
+ * <member href="http://example.org/2"
+ * hrefreadonly="http://example.com/2/bar"
+ * title="Sample 2"
+ * updated="2003-12-13T18:30:02Z" />
+ * <member href="http://example.org/3"
+ * hrefreadonly="http://example.com/3/bar"
+ * title="Sample 3"
+ * updated="2003-12-13T18:30:02Z" />
+ * <member href="http://example.org/4"
+ * title="Sample 4"
+ * updated="2003-12-13T18:30:02Z" />
+ * </collection>
+ */
+public class AtomCollection
+{
+ public static final Namespace ns =
+ Namespace.getNamespace("http://purl.org/atom/app#");
+ private static SimpleDateFormat df =
+ new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ssZ" );
+ private String next = null;
+ private List members = new ArrayList();
+
+ public AtomCollection()
+ {
+ }
+
+ /** URI of collection containing member elements updated earlier in time */
+ public String getNext()
+ {
+ return next;
+ }
+
+ public void setNext(String next)
+ {
+ this.next = next;
+ }
+
+ public List getMembers()
+ {
+ return members;
+ }
+
+ public void setMembers(List members)
+ {
+ this.members = members;
+ }
+
+ public void addMember(Member member)
+ {
+ members.add(member);
+ }
+
+ /** Models an Atom collection member */
+ /*
+ * appMember = element app:member { attribute title { text }, attribute href {
+ * text }, attribute hrefreadonly { text } ?, attribute updated { text } }
+ */
+ public static class Member
+ {
+ private String title;
+ private String href;
+ private String hrefreadonly;
+ private Date updated;
+
+ public Member()
+ {
+ }
+
+ /** Human readable title */
+ public String getTitle()
+ {
+ return title;
+ }
+
+ public void setTitle(String title)
+ {
+ this.title = title;
+ }
+
+ /** The URI used to edit the member source */
+ public String getHref()
+ {
+ return href;
+ }
+
+ public void setHref(String href)
+ {
+ this.href = href;
+ }
+
+ /** The URI for readonly access to member source */
+ public String getHrefreadonly()
+ {
+ return hrefreadonly;
+ }
+
+ public void setHrefreadonly(String hrefreadonly)
+ {
+ this.hrefreadonly = hrefreadonly;
+ }
+
+ /** Same as updated value of collection member */
+ public Date getUpdated()
+ {
+ return updated;
+ }
+
+ public void setUpdated(Date updated)
+ {
+ this.updated = updated;
+ }
+ }
+
+ /** Deserialize an Atom Collection XML document into an object */
+ public static AtomCollection documentToCollection(Document document)
+ throws Exception
+ {
+ AtomCollection collection = new AtomCollection();
+ Element root = document.getRootElement();
+ if (root.getAttribute("next") != null)
+ {
+ collection.setNext(root.getAttribute("next").getValue());
+ }
+ List mems = root.getChildren("member", ns);
+ Iterator iter = mems.iterator();
+ while (iter.hasNext())
+ {
+ Element e = (Element) iter.next();
+ collection.addMember(AtomCollection.elementToMember(e));
+ }
+ return collection;
+ }
+
+ /** Serialize an AtomCollection object into an XML document */
+ public static Document collectionToDocument(AtomCollection collection)
+ {
+ Document doc = new Document();
+ Element root = new Element("collection", ns);
+ doc.setRootElement(root);
+ if (collection.getNext() != null)
+ {
+ root.setAttribute("next", collection.getNext());
+ }
+ Iterator iter = collection.getMembers().iterator();
+ while (iter.hasNext())
+ {
+ Member member = (Member) iter.next();
+ root.addContent(AtomCollection.memberToElement(member));
+ }
+ return doc;
+ }
+
+ /** Deserialize an Atom collection member XML element into an object */
+ public static Member elementToMember(Element element) throws Exception
+ {
+ Member member = new Member();
+ member.setTitle(element.getAttribute("title").getValue());
+ member.setHref(element.getAttribute("href").getValue());
+ if (element.getAttribute("href") != null)
+ {
+ member.setHref(element.getAttribute("href").getValue());
+ }
+ member.setUpdated(df.parse(element.getAttribute("updated").getValue()));
+ return member;
+ }
+
+ /** Serialize a collection member into an XML element */
+ public static Element memberToElement(Member member)
+ {
+ Element element = new Element("member", ns);
+ element.setAttribute("title", member.getTitle()); // TODO: escape/strip HTML?
+ element.setAttribute("href", member.getHref());
+ if (member.getHrefreadonly() != null)
+ {
+ element.setAttribute("hrefreadonly", member.getHrefreadonly());
+ }
+ element.setAttribute("updated", df.format(member.getUpdated()));
+ return element;
+ }
+
+ /** Start and end date range */
+ public static class Range { Date start=null; Date end=null; }
+
+ /** Parse HTTP Range header into a start and end date range */
+ public static Range parseRange(String rangeString) throws ParseException
+ {
+ // Range: updated=<isodate>/<isodate>
+ // Range: updated=<isodate>/
+ // Range: updated=/<isodate>
+
+ Range range = new Range();
+ String[] split = rangeString.split("=");
+ if (split[1].startsWith("/"))
+ {
+ // we have only end date
+ range.end = df.parse(split[1].split("/")[1]);
+ }
+ else if (split[1].endsWith("/"))
+ {
+ // we have only start date
+ range.start = df.parse(split[1].split("/")[0]);
+ }
+ else
+ {
+ // both dates present
+ String[] dates = split[1].split("/");
+ range.start = df.parse(dates[0]);
+ range.end = df.parse(dates[1]);
+ }
+ return range;
+ }
+}
Added: incubator/roller/branches/roller_1.x/src/org/roller/presentation/atomapi/AtomHandler.java
URL: http://svn.apache.org/viewcvs/incubator/roller/branches/roller_1.x/src/org/roller/presentation/atomapi/AtomHandler.java?rev=327589&view=auto
==============================================================================
--- incubator/roller/branches/roller_1.x/src/org/roller/presentation/atomapi/AtomHandler.java (added)
+++ incubator/roller/branches/roller_1.x/src/org/roller/presentation/atomapi/AtomHandler.java Fri Oct 21 14:27:36 2005
@@ -0,0 +1,124 @@
+/*
+ * 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.atomapi;
+
+import java.io.InputStream;
+import java.util.Date;
+
+import com.sun.syndication.feed.atom.Entry;
+
+/**
+ * Interface to be supported by an Atom server, expected lifetime: one request.
+ * AtomServlet calls this generic interface instead of Roller specific APIs.
+ * Does not impose any specific set of collections, just three collection types:
+ * entries, resources and categories. Implementations determine what collections
+ * of each type exist and what URIs are used to get and edit them.
+ * <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
+ */
+ public AtomCollection getCollection(String[] pathInfo) throws Exception;
+
+ /**
+ * Return collection restricted by date range
+ * @param pathInfo Used to determine which collection
+ * @param start Start date or null if none
+ * @param end End date or null of none
+ * @param offset Offset into query results (or -1 if none)
+ */
+ public AtomCollection getCollection(
+ String[] pathInfo, Date start, Date end, int offset)
+ 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 String postResource(String[] pathInfo, String name, String contentType,
+ InputStream is) throws Exception;
+
+ /**
+ * Update a resource.
+ * @param pathInfo Path info portion of URL
+ */
+ public void putResource(String[] pathInfo, String contentType,
+ InputStream is) throws Exception;
+
+ /**
+ * Delete resource specified by pathInfo.
+ * @param pathInfo Path info portion of URL
+ */
+ public void deleteResource(String[] pathInfo) throws Exception;
+
+ /**
+ * Get resource file path (so Servlet can determine MIME type).
+ * @param pathInfo Path info portion of URL
+ */
+ public String getResourceFilePath(String[] pathInfo) throws Exception;
+
+ public boolean isIntrospectionURI(String [] pathInfo);
+
+ public boolean isCollectionURI(String [] pathInfo);
+ public boolean isEntryCollectionURI(String [] pathInfo);
+ public boolean isResourceCollectionURI(String [] pathInfo);
+ public boolean isCategoryCollectionURI(String [] pathInfo);
+
+ public boolean isEntryURI(String[] pathInfo);
+ public boolean isResourceURI(String[] pathInfo);
+ public boolean isCategoryURI(String[] pathInfo);
+}
+
Added: incubator/roller/branches/roller_1.x/src/org/roller/presentation/atomapi/AtomService.java
URL: http://svn.apache.org/viewcvs/incubator/roller/branches/roller_1.x/src/org/roller/presentation/atomapi/AtomService.java?rev=327589&view=auto
==============================================================================
--- incubator/roller/branches/roller_1.x/src/org/roller/presentation/atomapi/AtomService.java (added)
+++ incubator/roller/branches/roller_1.x/src/org/roller/presentation/atomapi/AtomService.java Fri Oct 21 14:27:36 2005
@@ -0,0 +1,273 @@
+/*
+ * 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.atomapi;
+
+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-04.txt
+ *
+ * appService =
+ * element app:service {
+ * (appWorkspace* & anyElement* )
+ * }
+ *
+ * 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
+ * contents="entries" title="My Blog Entries"
+ * href="http://example.org/reilly/feed" />
+ * <collection contents="generic"
+ * title="Documents" href="http://example.org/reilly/pic" />
+ * </workspace>
+ * <workspace title="Side Bar Blog">
+ * <collection contents="entries"
+ * title="Entries" href="http://example.org/reilly/feed" />
+ * <collection
+ * contents="http://example.net/booklist" title="Books"
+ * href="http://example.org/reilly/books" />
+ * </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 { attribute title { text },
+ * attribute contents { text }, attribute href { text }, anyElement* }
+ */
+ public static class Collection
+ {
+ private String title;
+ private String contents = "generic";
+ private String href;
+
+ public Collection()
+ {
+ }
+
+ /**
+ * Contents attribute conveys the nature of a collection's member
+ * resources. May be "entry" or "generic" and defaults to "generic"
+ */
+ public String getContents()
+ {
+ return contents;
+ }
+
+ public void setContents(String contents)
+ {
+ this.contents = contents;
+ }
+
+ /** 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());
+ if (element.getAttribute("href") != null)
+ {
+ collection.setContents(element.getAttribute("contents").getValue());
+ }
+ 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());
+ if (collection.getContents() != null)
+ {
+ element.setAttribute("contents", collection.getContents());
+ }
+ return element;
+ }
+}
\ No newline at end of file
Added: incubator/roller/branches/roller_1.x/src/org/roller/presentation/atomapi/AtomServlet.java
URL: http://svn.apache.org/viewcvs/incubator/roller/branches/roller_1.x/src/org/roller/presentation/atomapi/AtomServlet.java?rev=327589&view=auto
==============================================================================
--- incubator/roller/branches/roller_1.x/src/org/roller/presentation/atomapi/AtomServlet.java (added)
+++ incubator/roller/branches/roller_1.x/src/org/roller/presentation/atomapi/AtomServlet.java Fri Oct 21 14:27:36 2005
@@ -0,0 +1,389 @@
+/*
+ * 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.atomapi;
+
+import java.io.FileInputStream;
+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 org.roller.util.Utilities;
+
+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;
+
+/**
+ * Atom Servlet implements Atom by calling a Roller independent handler.
+ * @web.servlet name="AtomServlet"
+ * @web.servlet-mapping url-pattern="/atom/*"
+ * @author David M Johnson
+ */
+public class AtomServlet extends HttpServlet
+{
+ /** We use Rome to parse/generate Atom feeds and it does Atom format 0.3 */
+ public static final String FEED_TYPE = "atom_0.3";
+
+ private static Log mLogger =
+ LogFactory.getFactory().getInstance(AtomServlet.class);
+
+ //-----------------------------------------------------------------------------
+ /**
+ * Create an Atom request handler.
+ * TODO: make AtomRequestHandler implementation configurable.
+ */
+ private AtomHandler createAtomRequestHandler(HttpServletRequest request)
+ {
+ 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);
+ 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
+ String ranges = req.getHeader("Range");
+ if (ranges == null) req.getParameter("Range");
+ AtomCollection col = null;
+ if (ranges != null)
+ {
+ AtomCollection.Range range =
+ AtomCollection.parseRange(req.getHeader("Range"));
+ int offset = 0;
+ String offsetString = req.getParameter("offset");
+ if (offsetString != null)
+ {
+ offset = Integer.parseInt(offsetString);
+ }
+ col= handler.getCollection(
+ pathInfo, range.start, range.end, offset);
+ }
+ else
+ {
+ col= handler.getCollection(pathInfo);
+ }
+ Document doc = AtomCollection.collectionToDocument(col);
+ 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.isEntryURI(pathInfo))
+ {
+ // return an entry
+ Entry entry = handler.getEntry(pathInfo);
+ Writer writer = res.getWriter();
+ serializeEntry(entry, writer);
+ writer.close();
+ }
+ else if (handler.isResourceURI(pathInfo))
+ {
+ // return a resource
+ String absPath = handler.getResourceFilePath(pathInfo);
+ String type = getServletContext().getMimeType(absPath);
+ res.setContentType(type);
+ Utilities.copyInputToOutput(
+ new FileInputStream(absPath), res.getOutputStream());
+ }
+ 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.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();
+ while (links.hasNext()) {
+ Link link = (Link) links.next();
+ if (link.getRel().equals("alternate")) {
+ res.addHeader("Location", link.getHref());
+ break;
+ }
+ }
+ // write entry back out to response
+ res.setStatus(HttpServletResponse.SC_CREATED);
+ Writer writer = res.getWriter();
+ serializeEntry(savedEntry, writer);
+ writer.close();
+ }
+ else if (handler.isResourceCollectionURI(pathInfo))
+ {
+ // get incoming file name from HTTP header
+ String name = req.getHeader("Name");
+
+ // hand input stream of to hander to post file
+ String location = handler.postResource(
+ pathInfo, name, req.getContentType(), req.getInputStream());
+ res.setStatus(HttpServletResponse.SC_CREATED);
+ res.setHeader("Location", location);
+ }
+ 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.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
+ Writer writer = res.getWriter();
+ serializeEntry(updatedEntry, writer);
+ res.setStatus(HttpServletResponse.SC_OK);
+ writer.close();
+ }
+ else if (handler.isResourceCollectionURI(pathInfo))
+ {
+ // handle input stream to handler
+ handler.putResource(
+ pathInfo, req.getContentType(), req.getInputStream());
+ 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.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.isResourceURI(pathInfo))
+ {
+ handler.deleteResource(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.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,"/");
+ }
+
+ /**
+ * Utility method to make up for a Rome shortcoming:
+ * Rome can only serialize entire feeds, not individual elements
+ */
+ 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);
+ XMLOutputter outputter = new XMLOutputter();
+ outputter.setFormat(Format.getPrettyFormat());
+ outputter.output(entryElement, writer);
+ }
+
+ /**
+ * Utility method to make up for a Rome shortcoming:
+ * Rome can only parse Atom data with XML document root 'feed'
+ */
+ 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);
+
+ WireFeedInput input = new WireFeedInput();
+ Feed parsedFeed = (Feed)input.build(feedDoc);
+ return (Entry)parsedFeed.getEntries().get(0);
+ }
+}