View Javadoc

1   package org.apache.velocity.site.news.plugin;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.BufferedWriter;
23  import java.io.File;
24  import java.io.FileOutputStream;
25  import java.io.IOException;
26  import java.io.OutputStreamWriter;
27  import java.io.StringReader;
28  import java.io.StringWriter;
29  import java.io.Writer;
30  import java.util.ArrayList;
31  import java.util.Collections;
32  import java.util.Iterator;
33  import java.util.List;
34  
35  import org.apache.commons.io.IOUtils;
36  import org.apache.commons.lang.StringUtils;
37  import org.apache.maven.doxia.module.xhtml.XhtmlSink;
38  import org.apache.maven.doxia.module.xhtml.decoration.render.RenderingContext;
39  import org.apache.maven.doxia.parser.ParseException;
40  import org.apache.maven.doxia.sink.Sink;
41  import org.apache.velocity.site.news.VelocityNewsException;
42  import org.apache.velocity.site.news.VelocityNewsUtils;
43  import org.apache.velocity.site.news.model.Item;
44  
45  import com.sun.syndication.feed.synd.SyndCategory;
46  import com.sun.syndication.feed.synd.SyndCategoryImpl;
47  import com.sun.syndication.feed.synd.SyndContent;
48  import com.sun.syndication.feed.synd.SyndContentImpl;
49  import com.sun.syndication.feed.synd.SyndEntry;
50  import com.sun.syndication.feed.synd.SyndEntryImpl;
51  import com.sun.syndication.feed.synd.SyndFeed;
52  import com.sun.syndication.feed.synd.SyndFeedImpl;
53  import com.sun.syndication.io.FeedException;
54  import com.sun.syndication.io.SyndFeedOutput;
55  
56  /**
57   * Generate the RSS News Feed.
58   *
59   * @author <a href="mailto:henning@apache.org">Henning P. Schmiedehausen</a>
60   * @version $Revision: 526767 $
61   *
62   */
63  public class NewsFeedGenerator
64  {
65  
66      private final NewsFeed newsFeed;
67  
68      private SyndFeed feed = null;
69  
70      private List feedEntries = new ArrayList();
71  
72      public NewsFeedGenerator(final NewsFeed newsFeed)
73      {
74          this.newsFeed = newsFeed;
75      }
76  
77      public void openFeed()
78      {
79  
80          feed = new SyndFeedImpl();
81          feed.setFeedType("rss_2.0");
82          feed.setEncoding("UTF-8");
83  
84          // Todo Localization ?
85          String projectName = newsFeed.getProject().getName();
86          feed.setTitle(projectName + " News");
87          feed.setDescription("Recent news from " + projectName);
88  
89          String link = newsFeed.getProject().getUrl();
90          if (StringUtils.isNotEmpty(link))
91          {
92              feed.setLink(link);
93          }
94      }
95  
96      public void publishItem(final Item item) throws VelocityNewsException
97      {
98          if (feed != null)
99          {
100             SyndEntry entry = new SyndEntryImpl();
101             entry.setTitle(item.getHeadline());
102 
103             String itemPath = newsFeed.getOutputName() + ".html#" + item.getId();
104 
105             if (StringUtils.isNotEmpty(newsFeed.getBaseUrl()))
106             {
107                 entry.setLink(newsFeed.getBaseUrl() + itemPath);
108             }
109             else
110             {
111                 // Strictly spoken this is not a legal RSS feed.
112                 entry.setLink(itemPath);
113             }
114 
115             if (item.getDate() != null)
116             {
117                 entry.setPublishedDate(VelocityNewsUtils.parseItemDate(item.getDate()));
118             }
119 
120             SyndContent content = new SyndContentImpl();
121             content.setType("text/html");
122             content.setValue(getAsHtml(item.getText()));
123 
124             entry.setDescription(content);
125 
126             List itemCategories = item.getCategories();
127 
128             if (itemCategories != null)
129             {
130                 List categories = new ArrayList(itemCategories.size());
131 
132                 for (Iterator it = itemCategories.iterator(); it.hasNext();)
133                 {
134                     String category = (String) it.next();
135 
136                     SyndCategory syndCategory = new SyndCategoryImpl();
137                     syndCategory.setName(category);
138                     categories.add(syndCategory);
139                 }
140 
141                 entry.setCategories(categories);
142             }
143 
144             feedEntries.add(entry);
145         }
146     }
147 
148     public void publishFeed() throws VelocityNewsException
149     {
150         if (feed != null)
151         {
152 
153             File dir = new File(newsFeed.getOutputDirectory(), "/rss");
154 
155             if (!dir.exists())
156             {
157                 if (!dir.mkdir())
158                 {
159                     throw new VelocityNewsException("Could not create RSS dir " + dir);
160                 }
161             }
162             if (!dir.canWrite() || !dir.isDirectory())
163             {
164                 throw new VelocityNewsException("RSS dir " + dir
165                         + " already exists but it is either no directory or not writeable!");
166             }
167 
168             File feedFile = new File(dir, "/" + newsFeed.getOutputName() + ".rss");
169 
170             if (feedFile.exists() && !feedFile.canWrite())
171             {
172                 throw new VelocityNewsException("Can not write to the RSS file " + feedFile);
173             }
174 
175             FileOutputStream fos = null;
176             OutputStreamWriter osw = null;
177             BufferedWriter bw = null;
178 
179             try
180             {
181                 fos = new FileOutputStream(feedFile);
182                 osw = new OutputStreamWriter(fos, "UTF-8");
183                 bw = new BufferedWriter(osw);
184 
185                 SyndFeedOutput output = new SyndFeedOutput();
186                 try
187                 {
188                     feed.setEntries(feedEntries);
189                     output.output(feed, bw);
190                 }
191                 catch (FeedException fe)
192                 {
193                     throw new VelocityNewsException("While publishing Feed: ", fe);
194                 }
195 
196                 bw.flush();
197             }
198             catch (IOException ioe)
199             {
200                 throw new VelocityNewsException("While writing feed: ", ioe);
201             }
202             finally
203             {
204                 IOUtils.closeQuietly(bw);
205                 IOUtils.closeQuietly(osw);
206                 IOUtils.closeQuietly(fos);
207             }
208         }
209     }
210 
211     private String getAsHtml(final String itemText) throws VelocityNewsException
212     {
213 
214         String aptText = " " + itemText;
215 
216         StringWriter stringWriter = new StringWriter();
217         RenderingContext renderContext = new RenderingContext(new File(newsFeed.getOutputDirectory()), "/"
218                 + newsFeed.getOutputName() + ".html");
219 
220         Sink sink = new NewsItemSink(stringWriter, renderContext);
221 
222         try
223         {
224             newsFeed.getAptParser().parse(new StringReader(aptText), sink);
225         }
226         catch (ParseException pe)
227         {
228             throw new VelocityNewsException("While parsing Apt: ", pe);
229         }
230 
231         return stringWriter.toString();
232     }
233 
234     public static class NewsItemSink extends XhtmlSink
235     {
236         private NewsItemSink(final Writer writer, final RenderingContext renderingContext)
237         {
238             super(writer, renderingContext, Collections.EMPTY_MAP);
239         }
240 
241         public void head()
242         {
243             resetState();
244             setHeadFlag(true);
245         }
246 
247         public void head_()
248         {
249             setHeadFlag(false);
250         }
251 
252         public void body()
253         {
254         }
255 
256         public void body_()
257         {
258         }
259     }
260 }