1 package org.apache.velocity.site.news.plugin;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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
58
59
60
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
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
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 }