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}