001package net.sf.logdistiller.publishers;
002
003/*
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017import java.io.*;
018import java.util.*;
019
020import com.sun.syndication.feed.synd.*;
021import com.sun.syndication.io.FeedException;
022import com.sun.syndication.io.SyndFeedInput;
023import com.sun.syndication.io.SyndFeedOutput;
024
025import net.sf.logdistiller.*;
026
027/**
028 * Adds LogDistillation reports to a feed file (at the beginning). Any feed format supported by Rome can be used.
029 * <p>
030 * Parameters (as direct publisher param or global <code>feed.*</code> param):
031 * <ul>
032 * <li><code>filename</code> (default: <code>feed.xml</code>): the feed file name (relative to output directory)</li>
033 * <li><code>maxEntries</code> (default: 100): maximum entries in the feed file: if new entries are added, oldest ones
034 * are removed</li>
035 * </ul>
036 * <p>
037 * When the feed file must be created, these parameters are used:
038 * <ul>
039 * <li><code>type</code> (default: <code>atom_0.3</code>): the feed type (any value supported by Rome)</li>
040 * <li><code>title</code>: the feed title (default: "LogDistiller [logdistillation id]"</li>
041 * <li><code>link</code>: the feed link (default: LogDistiller's site)</li>
042 * <li><code>description</code>: the feed description (default: "LogDistiller reports for [logdistillation id]"</li>
043 * </ul>
044 *
045 * @since 0.7
046 */
047public class FeedPublisher
048    extends Publisher
049{
050    public final static String DEFAULT_FEED_TYPE = "atom_0.3";
051
052    public String getId()
053    {
054        return "feed";
055    }
056
057    public void publish( LogDistillation logdistillation, LogDistiller.Report report )
058        throws IOException, PublishException
059    {
060        // prepare the data for the entry
061        String content = logdistillation.getContent();
062        String subject =
063            "[logdistiller:" + logdistillation.getDefinition().getId() + "]"
064                + ( ( content == null ) ? "" : ( " " + content ) ) + " global report: "
065                + logdistillation.getEventCount() + " logevents";
066        ReportFormat format = ReportFormats.getReportFormat( report.getFormat() );
067        StringWriter sw = new StringWriter();
068        format.report( logdistillation, sw );
069        String body = sw.toString();
070        String url = logdistillation.getDefinition().getOutput().getUrl();
071
072        // create the new entry
073        SyndEntry entry = createEntry( subject, url, format.getContentType(), body );
074
075        // add it to the feed (creating it if necessary)
076        addEntry( entry, logdistillation, report );
077    }
078
079    public void publish( LogDistillation.Group group, LogDistiller.Report report )
080        throws IOException, PublishException
081    {
082        // prepare the data for the entry
083        LogDistillation logdistillation = group.getLogdistillation();
084        String content = logdistillation.getContent();
085        String subject =
086            "[logdistiller:" + logdistillation.getDefinition().getOutput().getId() + "]"
087                + ( ( content == null ) ? "" : ( " " + content ) ) + " report for group "
088                + group.getDefinition().getId() + ": " + group.getEventCount() + " logevents";
089        ReportFormat format = ReportFormats.getReportFormat( report.getFormat() );
090        StringWriter sw = new StringWriter();
091        format.report( group, sw );
092        String body = sw.toString();
093        String url = logdistillation.getDefinition().getOutput().getUrl();
094
095        // create the new entry
096        SyndEntry entry = createEntry( subject, url, format.getContentType(), body );
097
098        // add it to the feed (creating it if necessary)
099        addEntry( entry, logdistillation, report );
100    }
101
102    /**
103     * Create a new feed for the corresponding report.
104     *
105     * @param logdistillation
106     * @param report
107     * @return the new feed
108     */
109    private SyndFeed createFeed( LogDistillation logdistillation, LogDistiller.Report report )
110    {
111        String type = report.getParam( "type", DEFAULT_FEED_TYPE );
112        String title = report.getParam( "title", "Logdistiller " + logdistillation.getDefinition().getOutput().getId() );
113        String url = logdistillation.getDefinition().getOutput().getUrl();
114        String link = report.getParam( "link", ( url == null ) ? "http://logdistiller.sf.net/" : url );
115        String description =
116            report.getParam( "description", "LogDistiller reports for "
117                + logdistillation.getDefinition().getOutput().getId() );
118
119        SyndFeed feed = new SyndFeedImpl();
120        feed.setFeedType( type );
121        feed.setTitle( title );
122        feed.setLink( link );
123        feed.setDescription( description );
124
125        return feed;
126    }
127
128    /**
129     * Create a feed entry.
130     *
131     * @param title the title of the entry
132     * @param link the link of the entry (or <code>null</code>)
133     * @param contentType the content-type of the entry
134     * @param contentValue the content of the entry
135     * @return a new feed entry
136     */
137    private SyndEntry createEntry( String title, String link, String contentType, String contentValue )
138    {
139        SyndEntry entry = new SyndEntryImpl();
140        entry.setTitle( title );
141        if ( link != null )
142        {
143            entry.setLink( link );
144        }
145        entry.setPublishedDate( new Date() );
146
147        SyndContent content = new SyndContentImpl();
148        content.setType( contentType );
149        content.setValue( contentValue );
150        List<SyndContent> contents = new ArrayList<SyndContent>();
151        contents.add( content );
152
153        entry.setContents( contents );
154        return entry;
155    }
156
157    /**
158     * Add an entry to a feed report, creating the feed if necessary.
159     *
160     * @param entry the feed entry
161     * @param logdistillation the current logdistillation
162     * @param report the report configuration
163     * @throws IOException
164     * @throws PublishException
165     */
166    private void addEntry( SyndEntry entry, LogDistillation logdistillation, LogDistiller.Report report )
167        throws IOException, PublishException
168    {
169        String filename = report.getParam( "filename", "feed.xml" );
170        int maxEntries = Integer.parseInt( report.getParam( "maxEntries", "100" ) );
171        File file = logdistillation.newDestinationFile( filename );
172
173        // read or create the feed
174        SyndFeed feed;
175        if ( file.canRead() && ( file.length() > 0 ) )
176        {
177            SyndFeedInput input = new SyndFeedInput();
178            try
179            {
180                feed = input.build( file );
181            }
182            catch ( FeedException fe )
183            {
184                throw new PublishException( "error while reading feed " + filename, fe );
185            }
186        }
187        else
188        {
189            feed = createFeed( logdistillation, report );
190        }
191
192        // add the new entry
193        List<SyndEntry> entries = feed.getEntries();
194        entries.add( 0, entry );
195        if ( entries.size() > maxEntries )
196        {
197            entries.remove( entries.size() - 1 );
198        }
199        feed.setEntries( entries );
200
201        // write the feed content to the disk
202        SyndFeedOutput output = new SyndFeedOutput();
203        // write to a temporary file that will be swapped with the current file only on success
204        File tmpFile = logdistillation.newDestinationFile( filename + ".tmp" );
205        try
206        {
207            output.output( feed, tmpFile );
208            // ok, we were able to write the whole feed: now we can rotate with the final file
209            file.delete();
210            tmpFile.renameTo( file );
211        }
212        catch ( FeedException fe )
213        {
214            throw new PublishException( "error while writing temporary feed " + filename + ".tmp", fe );
215        }
216        finally
217        {
218            tmpFile.delete();
219        }
220    }
221}