001/* 002 Licensed to the Apache Software Foundation (ASF) under one 003 or more contributor license agreements. See the NOTICE file 004 distributed with this work for additional information 005 regarding copyright ownership. The ASF licenses this file 006 to you under the Apache License, Version 2.0 (the 007 "License"); you may not use this file except in compliance 008 with the License. You may obtain a copy of the License at 009 010 http://www.apache.org/licenses/LICENSE-2.0 011 012 Unless required by applicable law or agreed to in writing, 013 software distributed under the License is distributed on an 014 "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 KIND, either express or implied. See the License for the 016 specific language governing permissions and limitations 017 under the License. 018 */ 019package org.apache.wiki.rpc.atom; 020 021import org.apache.log4j.Logger; 022import org.apache.wiki.api.core.Context; 023import org.apache.wiki.api.core.Engine; 024import org.apache.wiki.api.core.Page; 025import org.apache.wiki.api.exceptions.ProviderException; 026import org.apache.wiki.api.exceptions.WikiException; 027import org.apache.wiki.api.spi.Wiki; 028import org.apache.wiki.pages.PageManager; 029import org.apache.wiki.plugin.WeblogEntryPlugin; 030import org.apache.wiki.plugin.WeblogPlugin; 031import org.apache.wiki.util.TextUtil; 032import org.intabulas.sandler.Sandler; 033import org.intabulas.sandler.SyndicationFactory; 034import org.intabulas.sandler.elements.Content; 035import org.intabulas.sandler.elements.Entry; 036import org.intabulas.sandler.elements.Feed; 037import org.intabulas.sandler.elements.Link; 038import org.intabulas.sandler.elements.Person; 039import org.intabulas.sandler.elements.impl.LinkImpl; 040import org.intabulas.sandler.exceptions.FeedMarshallException; 041 042import javax.servlet.ServletConfig; 043import javax.servlet.ServletException; 044import javax.servlet.http.HttpServlet; 045import javax.servlet.http.HttpServletRequest; 046import javax.servlet.http.HttpServletResponse; 047import java.io.IOException; 048import java.util.Collection; 049import java.util.Date; 050 051 052/** 053 * Handles incoming requests for the Atom API. This class uses the "sandler" Atom API implementation. 054 * 055 * @since 2.1.97 056 */ 057// FIXME: Rewrite using some other library 058public class AtomAPIServlet extends HttpServlet { 059 060 private static final Logger log = Logger.getLogger( AtomAPIServlet.class ); 061 062 private static final long serialVersionUID = 0L; 063 064 private Engine m_engine; 065 066 /** 067 * {@inheritDoc} 068 */ 069 @Override 070 public void init( final ServletConfig config ) throws ServletException { 071 m_engine = Wiki.engine().find( config ); 072 } 073 074 /** 075 * Takes the name of the page from the request URI. 076 * The initial slash is also removed. If there is no page, 077 * returns null. 078 */ 079 private String getPageName( final HttpServletRequest request ) { 080 String name = request.getPathInfo(); 081 if( name == null || name.length() <= 1 ) { 082 return null; 083 } else if( name.charAt( 0 ) == '/' ) { 084 name = name.substring( 1 ); 085 } 086 087 return TextUtil.urlDecodeUTF8( name ); 088 } 089 090 /** 091 * Implements the PostURI of the Atom spec. 092 * <p> 093 * Implementation notes: 094 * <ul> 095 * <li>Only fetches the first content. All other contents are ignored. 096 * <li>Assumes that incoming code is plain text or WikiMarkup, not html. 097 * </ul> 098 * 099 * {@inheritDoc} 100 */ 101 @Override 102 public void doPost( final HttpServletRequest request, final HttpServletResponse response ) throws ServletException { 103 log.debug( "Received POST to AtomAPIServlet" ); 104 105 try { 106 final String blogid = getPageName( request ); 107 final Page page = m_engine.getManager( PageManager.class ).getPage( blogid ); 108 if( page == null ) { 109 throw new ServletException( "Page " + blogid + " does not exist, cannot add blog post." ); 110 } 111 112 // FIXME: Do authentication here 113 final Entry entry = Sandler.unmarshallEntry( request.getInputStream() ); 114 115 // Fetch the obligatory parts of the content. 116 final Content title = entry.getTitle(); 117 final Content content = entry.getContent( 0 ); 118 final Person author = entry.getAuthor(); 119 120 // FIXME: Sandler 0.5 does not support generator 121 // Generate new blog entry. 122 final WeblogEntryPlugin plugin = new WeblogEntryPlugin(); 123 final String pageName = plugin.getNewEntryPage( m_engine, blogid ); 124 final String username = author.getName(); 125 final Page entryPage = Wiki.contents().page( m_engine, pageName ); 126 entryPage.setAuthor( username ); 127 128 final Context context = Wiki.context().create( m_engine, request, entryPage ); 129 final StringBuilder text = new StringBuilder(); 130 text.append( "!" ) 131 .append( title.getBody() ) 132 .append( "\n\n" ) 133 .append( content.getBody() ); 134 log.debug( "Writing entry: " + text ); 135 m_engine.getManager( PageManager.class ).saveText( context, text.toString() ); 136 } catch( final FeedMarshallException e ) { 137 log.error("Received faulty Atom entry",e); 138 throw new ServletException("Faulty Atom entry",e); 139 } catch( final IOException e ) { 140 log.error("I/O exception",e); 141 throw new ServletException("Could not get body of request",e); 142 } catch( final WikiException e ) { 143 log.error("Provider exception while posting",e); 144 throw new ServletException("JSPWiki cannot save the entry",e); 145 } 146 } 147 148 /** 149 * Handles HTTP GET. However, we do not respond to GET requests, 150 * other than to show an explanatory text. 151 * 152 * {@inheritDoc} 153 */ 154 @Override 155 public void doGet( final HttpServletRequest request, final HttpServletResponse response ) throws ServletException { 156 log.debug( "Received HTTP GET to AtomAPIServlet" ); 157 final String blogid = getPageName( request ); 158 log.debug( "Requested page " + blogid ); 159 try { 160 if( blogid == null ) { 161 final Feed feed = listBlogs(); 162 response.setContentType( "application/x.atom+xml; charset=UTF-8" ); 163 response.getWriter().println( Sandler.marshallFeed( feed ) ); 164 } else { 165 final Entry entry = getBlogEntry( blogid ); 166 response.setContentType( "application/x.atom+xml; charset=UTF-8" ); 167 response.getWriter().println( Sandler.marshallEntry( entry ) ); 168 } 169 response.getWriter().flush(); 170 } catch( final Exception e ) { 171 log.error( "Unable to generate response", e ); 172 throw new ServletException( "Internal problem - whack Janne on the head to get a better error report", e ); 173 } 174 } 175 176 private Entry getBlogEntry( final String entryid ) { 177 final Page page = m_engine.getManager( PageManager.class ).getPage( entryid ); 178 final Page firstVersion = m_engine.getManager( PageManager.class ).getPage( entryid, 1 ); 179 final Entry entry = SyndicationFactory.newSyndicationEntry(); 180 final String pageText = m_engine.getManager( PageManager.class ).getText(page.getName()); 181 final int firstLine = pageText.indexOf('\n'); 182 183 String title = ""; 184 if( firstLine > 0 ) { 185 title = pageText.substring( 0, firstLine ); 186 } 187 188 if( title.trim().length() == 0 ) { 189 title = page.getName(); 190 } 191 192 // Remove wiki formatting 193 while( title.startsWith("!") ) { 194 title = title.substring(1); 195 } 196 197 entry.setTitle( title ); 198 entry.setCreated( firstVersion.getLastModified() ); 199 entry.setModified( page.getLastModified() ); 200 entry.setAuthor( SyndicationFactory.createPerson( page.getAuthor(), null, null ) ); 201 entry.addContent( SyndicationFactory.createEscapedContent(pageText) ); 202 203 return entry; 204 } 205 206 /** 207 * Creates and outputs a full list of all available blogs 208 */ 209 private Feed listBlogs() throws ProviderException { 210 final Collection< Page > pages = m_engine.getManager( PageManager.class ).getAllPages(); 211 final Feed feed = SyndicationFactory.newSyndicationFeed(); 212 feed.setTitle("List of blogs at this site"); 213 feed.setModified( new Date() ); 214 215 for( final Page p : pages ) { 216 // List only weblogs 217 // FIXME: Unfortunately, a weblog is not known until it has een executed once, because plugins are off during the initial startup phase. 218 log.debug( p.getName() + " = " + p.getAttribute( WeblogPlugin.ATTR_ISWEBLOG ) ); 219 220 if( !( "true".equals( p.getAttribute( WeblogPlugin.ATTR_ISWEBLOG ) ) ) ) { 221 continue; 222 } 223 224 final String encodedName = TextUtil.urlEncodeUTF8( p.getName() ); 225 final Context context = Wiki.context().create( m_engine, p ); 226 final String title = TextUtil.replaceEntities( org.apache.wiki.rss.Feed.getSiteName( context ) ); 227 final Link postlink = createLink( "service.post", m_engine.getBaseURL() + "atom/" + encodedName, title ); 228 final Link editlink = createLink( "service.edit", m_engine.getBaseURL() + "atom/" + encodedName, title ); 229 final Link feedlink = createLink( "service.feed", m_engine.getBaseURL() + "atom.jsp?page=" + encodedName, title ); 230 231 feed.addLink( postlink ); 232 feed.addLink( feedlink ); 233 feed.addLink( editlink ); 234 } 235 236 return feed; 237 } 238 239 private Link createLink( final String rel, final String href, final String title ) { 240 final LinkImpl link = new LinkImpl(); 241 link.setRelationship( rel ); 242 link.setTitle( title ); 243 link.setType( "application/x.atom+xml" ); 244 link.setHref( href ); 245 246 return link; 247 } 248 249 /** 250 * {@inheritDoc} 251 */ 252 @Override 253 public void doDelete( final HttpServletRequest request, final HttpServletResponse response ) { 254 log.debug( "Received HTTP DELETE" ); 255 } 256 257 /** 258 * {@inheritDoc} 259 */ 260 @Override 261 public void doPut( final HttpServletRequest request, final HttpServletResponse response ) { 262 log.debug( "Received HTTP PUT" ); 263 } 264 265}