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