001    package 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    
017    import java.io.*;
018    import java.util.*;
019    
020    import com.sun.syndication.feed.synd.*;
021    import com.sun.syndication.io.FeedException;
022    import com.sun.syndication.io.SyndFeedInput;
023    import com.sun.syndication.io.SyndFeedOutput;
024    
025    import 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     */
047    public 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 contents = new ArrayList();
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 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    }