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 java.io.IOException;
022import java.util.Collection;
023import java.util.Date;
024import java.util.Iterator;
025
026import javax.servlet.ServletConfig;
027import javax.servlet.ServletException;
028import javax.servlet.http.HttpServlet;
029import javax.servlet.http.HttpServletRequest;
030import javax.servlet.http.HttpServletResponse;
031
032import org.apache.log4j.Logger;
033import org.apache.wiki.WikiContext;
034import org.apache.wiki.WikiEngine;
035import org.apache.wiki.WikiPage;
036import org.apache.wiki.api.exceptions.ProviderException;
037import org.apache.wiki.api.exceptions.WikiException;
038import org.apache.wiki.plugin.WeblogEntryPlugin;
039import org.apache.wiki.plugin.WeblogPlugin;
040import org.apache.wiki.util.TextUtil;
041import org.intabulas.sandler.Sandler;
042import org.intabulas.sandler.SyndicationFactory;
043import org.intabulas.sandler.elements.Content;
044import org.intabulas.sandler.elements.Entry;
045import org.intabulas.sandler.elements.Feed;
046import org.intabulas.sandler.elements.Link;
047import org.intabulas.sandler.elements.Person;
048import org.intabulas.sandler.exceptions.FeedMarshallException;
049
050
051/**
052 *  Handles incoming requests for the Atom API.  This class uses the
053 *  "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    static final Logger log = Logger.getLogger( AtomAPIServlet.class );
061
062    private static final long serialVersionUID = 0L;
063
064    private WikiEngine       m_engine;
065
066    /**
067     *  {@inheritDoc}
068     */
069    public void init( ServletConfig config )
070        throws ServletException
071    {
072        m_engine = WikiEngine.getInstance( 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( HttpServletRequest request )
081    {
082        String name = request.getPathInfo();
083
084        if( name == null || name.length() <= 1 )
085        {
086            return null;
087        }
088        else if( name.charAt(0) == '/' )
089        {
090            name = name.substring(1);
091        }
092
093        name = TextUtil.urlDecodeUTF8( name );
094
095        return name;
096    }
097
098    /**
099     *  Implements the PostURI of the Atom spec.
100     *  <p>
101     *  Implementation notes:
102     *  <ul>
103     *   <li>Only fetches the first content.  All other contents are ignored.
104     *   <li>Assumes that incoming code is plain text or WikiMarkup, not html.
105     *  </ul>
106     *  
107     *  @param request {@inheritDoc}
108     *  @param response {@inheritDoc}
109     *  @throws ServletException {@inheritDoc}
110     */
111    public void doPost( HttpServletRequest request, HttpServletResponse response )
112        throws ServletException
113    {
114        log.debug("Received POST to AtomAPIServlet");
115
116        try
117        {
118            String blogid = getPageName( request );
119
120            WikiPage page    = m_engine.getPage( blogid );
121
122            if( page == null )
123            {
124                throw new ServletException("Page "+blogid+" does not exist, cannot add blog post.");
125            }
126
127            //FIXME: Do authentication here
128            Entry entry = Sandler.unmarshallEntry( request.getInputStream() );
129
130            //
131            //  Fetch the obligatory parts of the content.
132            //
133            Content title   = entry.getTitle();
134            Content content = entry.getContent(0);
135
136            Person  author  = entry.getAuthor();
137
138            //FIXME: Sandler 0.5 does not support generator
139
140            //
141            //  Generate new blog entry.
142            //
143            WeblogEntryPlugin plugin = new WeblogEntryPlugin();
144
145            String pageName = plugin.getNewEntryPage( m_engine, blogid );
146            String username = author.getName();
147
148            WikiPage entryPage = new WikiPage( m_engine, pageName );
149            entryPage.setAuthor( username );
150
151            WikiContext context = new WikiContext( m_engine, request, entryPage );
152
153            StringBuilder text = new StringBuilder();
154            text.append( "!" + title.getBody() );
155            text.append( "\n\n" );
156            text.append( content.getBody() );
157
158            log.debug("Writing entry: "+text);
159
160            m_engine.saveText( context, text.toString() );
161
162        }
163        catch( FeedMarshallException e )
164        {
165            log.error("Received faulty Atom entry",e);
166            throw new ServletException("Faulty Atom entry",e);
167        }
168        catch( IOException e )
169        {
170            log.error("I/O exception",e);
171            throw new ServletException("Could not get body of request",e);
172        }
173        catch( WikiException e )
174        {
175            log.error("Provider exception while posting",e);
176            throw new ServletException("JSPWiki cannot save the entry",e);
177        }
178    }
179
180    /**
181     *  Handles HTTP GET.  However, we do not respond to GET requests,
182     *  other than to show an explanatory text.
183     *  
184     *  {@inheritDoc}
185     */
186    public void doGet( HttpServletRequest request, HttpServletResponse response )
187        throws ServletException
188    {
189        log.debug("Received HTTP GET to AtomAPIServlet");
190
191        String blogid = getPageName( request );
192
193        log.debug("Requested page "+blogid);
194
195        try
196        {
197            if( blogid == null )
198            {
199                Feed feed = listBlogs();
200
201                response.setContentType("application/x.atom+xml; charset=UTF-8");
202                response.getWriter().println( Sandler.marshallFeed(feed) );
203
204                response.getWriter().flush();
205            }
206            else
207            {
208                Entry entry = getBlogEntry( blogid );
209
210                response.setContentType("application/x.atom+xml; charset=UTF-8");
211                response.getWriter().println( Sandler.marshallEntry(entry) );
212
213                response.getWriter().flush();
214            }
215        }
216        catch( Exception e )
217        {
218            log.error("Unable to generate response",e);
219            throw new ServletException("Internal problem - whack Janne on the head to get a better error report",e);
220        }
221
222    }
223
224    private Entry getBlogEntry( String entryid )
225        throws ProviderException
226    {
227        WikiPage page = m_engine.getPage( entryid );
228        WikiPage firstVersion = m_engine.getPage( entryid, 1 );
229
230        Entry entry = SyndicationFactory.newSyndicationEntry();
231
232        String pageText = m_engine.getText(page.getName());
233        String title = "";
234        int firstLine = pageText.indexOf('\n');
235
236        if( firstLine > 0 )
237        {
238            title = pageText.substring( 0, firstLine );
239        }
240
241        if( title.trim().length() == 0 ) title = page.getName();
242
243        // Remove wiki formatting
244        while( title.startsWith("!") ) title = title.substring(1);
245
246        entry.setTitle( title );
247        entry.setCreated( firstVersion.getLastModified() );
248        entry.setModified( page.getLastModified() );
249        entry.setAuthor( SyndicationFactory.createPerson( page.getAuthor(),
250                                                          null,
251                                                          null ) );
252
253        entry.addContent( SyndicationFactory.createEscapedContent(pageText) );
254
255        return entry;
256    }
257
258    /**
259     *  Creates and outputs a full list of all available blogs
260     */
261    private Feed listBlogs()
262        throws ProviderException,
263               IOException
264    {
265        Collection< WikiPage > pages = m_engine.getPageManager().getAllPages();
266
267        Feed feed = SyndicationFactory.newSyndicationFeed();
268        feed.setTitle("List of blogs at this site");
269        feed.setModified( new Date() );
270
271        for( Iterator< WikiPage > i = pages.iterator(); i.hasNext(); )
272        {
273            WikiPage p = i.next();
274
275            //
276            //  List only weblogs
277            //  FIXME: Unfortunately, a weblog is not known until it has
278            //         been executed once, because plugins are off during
279            //         the initial startup phase.
280            //
281
282            log.debug( p.getName()+" = "+p.getAttribute(WeblogPlugin.ATTR_ISWEBLOG)) ;
283
284            if( !("true".equals(p.getAttribute(WeblogPlugin.ATTR_ISWEBLOG)) ) )
285                continue;
286
287            String encodedName = TextUtil.urlEncodeUTF8( p.getName() );
288
289            WikiContext context = new WikiContext( m_engine, p );
290
291            String title = TextUtil.replaceEntities(org.apache.wiki.rss.Feed.getSiteName(context));
292
293            Link postlink = createLink( "service.post",
294                                        m_engine.getBaseURL()+"atom/"+encodedName,
295                                        title );
296
297            Link editlink = createLink( "service.edit",
298                                        m_engine.getBaseURL()+"atom/"+encodedName,
299                                        title );
300
301            Link feedlink = createLink( "service.feed",
302                                        m_engine.getBaseURL()+"atom.jsp?page="+encodedName,
303                                        title );
304
305
306            feed.addLink( postlink );
307            feed.addLink( feedlink );
308            feed.addLink( editlink );
309        }
310
311        return feed;
312    }
313
314    private Link createLink( String rel,
315                             String href,
316                             String title )
317    {
318        org.intabulas.sandler.elements.impl.LinkImpl link = new org.intabulas.sandler.elements.impl.LinkImpl();
319
320        link.setRelationship( rel );
321        link.setTitle( title );
322        link.setType( "application/x.atom+xml" );
323        link.setHref( href );
324
325        return link;
326    }
327
328    /**
329     *  {@inheritDoc}
330     */
331    public void doDelete( HttpServletRequest request, HttpServletResponse response )
332        throws ServletException
333    {
334        log.debug("Received HTTP DELETE");
335    }
336
337    /**
338     *  {@inheritDoc}
339     */
340    public void doPut( HttpServletRequest request, HttpServletResponse response )
341        throws ServletException
342    {
343        log.debug("Received HTTP PUT");
344    }
345}