    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}