001: /**
002: * Copyright (c) 2003-2007, David A. Czarnecki
003: * All rights reserved.
004: *
005: * Redistribution and use in source and binary forms, with or without
006: * modification, are permitted provided that the following conditions are met:
007: *
008: * Redistributions of source code must retain the above copyright notice, this list of conditions and the
009: * following disclaimer.
010: * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
011: * following disclaimer in the documentation and/or other materials provided with the distribution.
012: * Neither the name of "David A. Czarnecki" and "blojsom" nor the names of its contributors may be used to
013: * endorse or promote products derived from this software without specific prior written permission.
014: * Products derived from this software may not be called "blojsom", nor may "blojsom" appear in their name,
015: * without prior written permission of David A. Czarnecki.
016: *
017: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
018: * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
019: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
020: * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
021: * EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
022: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
023: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
024: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
025: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
026: * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
027: * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
028: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
029: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
030: */package org.blojsom.extension.comment;
031:
032: import org.apache.commons.logging.Log;
033: import org.apache.commons.logging.LogFactory;
034: import org.blojsom.blog.Blog;
035: import org.blojsom.blog.Comment;
036: import org.blojsom.blog.Entry;
037: import org.blojsom.event.EventBroadcaster;
038: import org.blojsom.fetcher.Fetcher;
039: import org.blojsom.fetcher.FetcherException;
040: import org.blojsom.plugin.comment.event.CommentAddedEvent;
041: import org.blojsom.plugin.common.ResponseConstants;
042: import org.blojsom.servlet.ServletConfigFactoryBean;
043: import org.blojsom.util.BlojsomConstants;
044: import org.blojsom.util.BlojsomUtils;
045: import org.springframework.context.support.ClassPathXmlApplicationContext;
046: import org.w3c.dom.Document;
047: import org.w3c.dom.Element;
048: import org.w3c.dom.Node;
049: import org.w3c.dom.NodeList;
050: import org.xml.sax.SAXException;
051:
052: import javax.mail.internet.AddressException;
053: import javax.mail.internet.InternetAddress;
054: import javax.servlet.ServletConfig;
055: import javax.servlet.ServletException;
056: import javax.servlet.http.HttpServlet;
057: import javax.servlet.http.HttpServletRequest;
058: import javax.servlet.http.HttpServletResponse;
059: import javax.xml.parsers.DocumentBuilder;
060: import javax.xml.parsers.DocumentBuilderFactory;
061: import javax.xml.parsers.FactoryConfigurationError;
062: import javax.xml.parsers.ParserConfigurationException;
063: import java.io.IOException;
064: import java.io.PrintWriter;
065: import java.io.UnsupportedEncodingException;
066: import java.net.HttpURLConnection;
067: import java.util.Date;
068: import java.util.HashMap;
069:
070: /**
071: * blojsom Comment API Implementation
072: * <p/>
073: * <a href="http://wellformedweb.org/story/9">Comment API specification</a>.
074: * <p/>
075: * For more information on the <item/> fragment and its content, check the <a href="http://blogs.law.harvard.edu/tech/rss">RSS 2.0 specification</a>.
076: *
077: * @author David Czarnecki
078: * @author Mark Lussier
079: * @since blojsom 3.0
080: * @version $Id: CommentAPIServlet.java,v 1.4 2007/01/17 02:35:07 czarneckid Exp $
081: */
082: public class CommentAPIServlet extends HttpServlet {
083:
084: private Log _logger = LogFactory.getLog(CommentAPIServlet.class);
085:
086: /**
087: * RSS <item/> fragment tag containing the title
088: */
089: private static final String COMMENTAPI_TITLE = "title";
090:
091: /**
092: * RSS <item/> fragment tag containing the link
093: */
094: private static final String COMMENTAPI_LINK = "link";
095:
096: /**
097: * RSS <item/> fragment tag containing the description
098: */
099: private static final String COMMENTAPI_DESCRIPTION = "description";
100:
101: /**
102: * RSS <item/> fragment tag containing the author
103: */
104: private static final String COMMENTAPI_AUTHOR = "author";
105:
106: /**
107: * RSS <dc:creator/> fragment tag containing the author's name
108: */
109: private static final String COMMENTAPI_DC_CREATOR = "dc:creator";
110:
111: private static final String COMMENTAPI_TITLE_METADATA = "comment-api-metadata-title";
112:
113: private static final String COMMENTAPI_ACCEPTS_ONLY_POSTS_MESSAGE = "Comment API server only accepts POST requests.";
114:
115: private String[] BLOJSOM_CONFIGURATION_FILES = { "blojsom-commentapi.xml" };
116:
117: private ClassPathXmlApplicationContext _classPathXmlApplicationContext;
118:
119: /**
120: * Default constructor
121: */
122: public CommentAPIServlet() {
123: }
124:
125: /**
126: * Initialize the blojsom Comment API servlet
127: *
128: * @param servletConfig Servlet configuration information
129: * @throws ServletException If there is an error initializing the servlet
130: */
131: public void init(ServletConfig servletConfig)
132: throws ServletException {
133: super .init(servletConfig);
134:
135: ServletConfigFactoryBean.setServletConfig(servletConfig);
136:
137: _classPathXmlApplicationContext = new ClassPathXmlApplicationContext(
138: BLOJSOM_CONFIGURATION_FILES);
139: servletConfig
140: .getServletContext()
141: .setAttribute(
142: BlojsomConstants.BLOJSOM_COMMENTAPI_APPLICATION_CONTEXT,
143: _classPathXmlApplicationContext);
144:
145: if (_logger.isDebugEnabled()) {
146: _logger
147: .debug("blojsom Comment API: All Your Blog Are Belong To Us");
148: }
149: }
150:
151: /**
152: * Service a Comment API request
153: *
154: * @param httpServletRequest Request
155: * @param httpServletResponse Response
156: * @throws ServletException If there is an error processing the request
157: * @throws IOException If there is an error during I/O
158: */
159: protected void service(HttpServletRequest httpServletRequest,
160: HttpServletResponse httpServletResponse)
161: throws ServletException, IOException {
162: try {
163: httpServletRequest
164: .setCharacterEncoding(BlojsomConstants.UTF8);
165: } catch (UnsupportedEncodingException e) {
166: _logger.error(e);
167: }
168:
169: if (!"post".equalsIgnoreCase(httpServletRequest.getMethod())) {
170: httpServletResponse
171: .setContentType("text/html; charset=UTF-8");
172: httpServletResponse
173: .setContentLength(COMMENTAPI_ACCEPTS_ONLY_POSTS_MESSAGE
174: .length());
175: httpServletResponse
176: .setStatus(HttpURLConnection.HTTP_BAD_METHOD);
177: PrintWriter printWriter = httpServletResponse.getWriter();
178: printWriter.print(COMMENTAPI_ACCEPTS_ONLY_POSTS_MESSAGE);
179: printWriter.flush();
180:
181: return;
182: }
183:
184: String commentAuthor = null;
185: String commentEmail = null;
186: String commentLink = null;
187: String commentText = null;
188: String commentTitle = null;
189: String commentDCCreator = null;
190:
191: // Determine the appropriate user from the URL
192: String blogId = BlojsomUtils.getBlogFromPath(httpServletRequest
193: .getPathInfo());
194: if (BlojsomUtils.checkNullOrBlank(blogId) || "/".equals(blogId)) {
195: httpServletResponse.sendError(
196: HttpServletResponse.SC_NOT_FOUND,
197: "Requested blog not found: " + blogId);
198:
199: return;
200: }
201:
202: Fetcher fetcher = (Fetcher) _classPathXmlApplicationContext
203: .getBean("fetcher");
204:
205: Blog blog;
206: try {
207: blog = fetcher.loadBlog(blogId);
208: } catch (FetcherException e) {
209: if (_logger.isErrorEnabled()) {
210: _logger.error(e);
211: }
212:
213: return;
214: }
215:
216: if (blog.getProperty(BlojsomConstants.USE_DYNAMIC_BLOG_URLS) != null) {
217: BlojsomUtils.resolveDynamicBaseAndBlogURL(
218: httpServletRequest, blog, blogId);
219: }
220:
221: if (blog.getBlogCommentsEnabled().booleanValue()
222: && httpServletRequest.getContentLength() > 0) {
223: String permalink = httpServletRequest
224: .getParameter(BlojsomConstants.PERMALINK_PARAM);
225:
226: try {
227: DocumentBuilder builder = DocumentBuilderFactory
228: .newInstance().newDocumentBuilder();
229: Document document = builder.parse(httpServletRequest
230: .getInputStream());
231:
232: // Walk through the RSS2 Item Fragment
233: Element docElement = document.getDocumentElement();
234: if (docElement.hasChildNodes()) {
235: NodeList comment = docElement.getChildNodes();
236: if (comment.getLength() > 0) {
237: for (int x = 0; x < comment.getLength(); x++) {
238: Node node = comment.item(x);
239: if (node.getNodeType() == Node.ELEMENT_NODE) {
240: if (node.getNodeName().equals(
241: COMMENTAPI_LINK)
242: && (node.getFirstChild() != null)) {
243: commentLink = node.getFirstChild()
244: .getNodeValue();
245: }
246:
247: if (node.getNodeName().equals(
248: COMMENTAPI_TITLE)
249: && (node.getFirstChild() != null)) {
250: commentTitle = node.getFirstChild()
251: .getNodeValue();
252: }
253:
254: if (node.getNodeName().equals(
255: COMMENTAPI_AUTHOR)
256: && (node.getFirstChild() != null)) {
257: commentAuthor = node
258: .getFirstChild()
259: .getNodeValue();
260: }
261:
262: if (node.getNodeName().equals(
263: COMMENTAPI_DESCRIPTION)
264: && (node.getFirstChild() != null)) {
265: commentText = node.getFirstChild()
266: .getNodeValue();
267: }
268:
269: if (node.getNodeName().equals(
270: COMMENTAPI_DC_CREATOR)
271: && (node.getFirstChild() != null)) {
272: commentDCCreator = node
273: .getFirstChild()
274: .getNodeValue();
275: }
276: }
277: }
278: }
279: }
280: } catch (ParserConfigurationException e) {
281: if (_logger.isErrorEnabled()) {
282: _logger.error(e);
283: }
284: httpServletResponse.sendError(
285: HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e
286: .getMessage());
287:
288: return;
289: } catch (FactoryConfigurationError e) {
290: if (_logger.isErrorEnabled()) {
291: _logger.error(e);
292: }
293: httpServletResponse.sendError(
294: HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e
295: .getMessage());
296:
297: return;
298: } catch (SAXException e) {
299: if (_logger.isErrorEnabled()) {
300: _logger.error(e);
301: }
302: httpServletResponse.sendError(
303: HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e
304: .getMessage());
305:
306: return;
307: } catch (IOException e) {
308: if (_logger.isErrorEnabled()) {
309: _logger.error(e);
310: }
311: httpServletResponse.sendError(
312: HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e
313: .getMessage());
314:
315: return;
316: }
317:
318: // Try to extract an email address from "User Name <useremail@.com>" formatted string,
319: // otherwise, just use the Name
320: if (commentAuthor != null) {
321: try {
322: InternetAddress emailaddress = new InternetAddress(
323: commentAuthor);
324: commentEmail = emailaddress.getAddress();
325: commentAuthor = emailaddress.getPersonal();
326: if (commentAuthor == null) {
327: commentAuthor = commentEmail;
328: }
329: } catch (AddressException e) {
330: if (_logger.isErrorEnabled()) {
331: _logger.error(e);
332: }
333: }
334: } else {
335: commentAuthor = "";
336: commentEmail = "";
337: }
338:
339: // If the link is null, set it to an empty string
340: if (commentLink == null) {
341: commentLink = "";
342: }
343:
344: // If the dc:creator element is available, assign that to the author name
345: if (commentDCCreator != null) {
346: commentAuthor = commentDCCreator;
347: }
348:
349: if (commentText != null) {
350: if (_logger.isDebugEnabled()) {
351: _logger
352: .debug("Comment API ==============================================");
353: _logger.debug(" Blog: " + blog.getBlogId());
354: _logger.debug(" Permalink: " + permalink);
355: _logger.debug(" Commenter: " + commentAuthor);
356: _logger.debug("Cmtr Email: " + commentEmail);
357: _logger.debug(" Link: " + commentLink);
358: _logger.debug(" Comment: \n" + commentText);
359: }
360:
361: // Create a new blog comment
362: Comment comment = fetcher.newComment();
363: try {
364: Entry entry = fetcher.loadEntry(blog, permalink);
365:
366: HashMap commentMetaData = new HashMap();
367: commentMetaData.put(COMMENTAPI_TITLE_METADATA,
368: commentTitle);
369:
370: comment.setAuthor(commentAuthor);
371: comment.setAuthorEmail(commentEmail);
372: comment.setAuthorURL(commentLink);
373: comment.setBlogId(blog.getId());
374: comment.setComment(commentText);
375: comment.setCommentDate(new Date());
376: comment.setEntry(entry);
377: comment.setIp(httpServletRequest.getRemoteAddr());
378: comment.setMetaData(commentMetaData);
379: comment.setStatus(ResponseConstants.NEW_STATUS);
380:
381: fetcher.saveComment(blog, comment);
382:
383: EventBroadcaster eventBroadcaster = (EventBroadcaster) _classPathXmlApplicationContext
384: .getBean("eventBroadcaster");
385:
386: eventBroadcaster
387: .broadcastEvent(new CommentAddedEvent(this ,
388: new Date(), comment, blog));
389:
390: httpServletResponse
391: .setStatus(HttpServletResponse.SC_OK);
392: } catch (FetcherException e) {
393: _logger.error(e);
394:
395: httpServletResponse.sendError(
396: HttpServletResponse.SC_NOT_FOUND,
397: "Could not find blog entry: " + permalink);
398: }
399: } else {
400: httpServletResponse.sendError(
401: HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
402: "No comment text available");
403: }
404: } else {
405: httpServletResponse
406: .sendError(
407: HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
408: "Blog comments not enabled or No content in request");
409: }
410: }
411:
412: /**
413: * Called when removing the servlet from the servlet container
414: */
415: public void destroy() {
416: super .destroy();
417:
418: _classPathXmlApplicationContext.destroy();
419:
420: if (_logger.isDebugEnabled()) {
421: _logger.debug("blojsom Comment API destroyed");
422: }
423: }
424: }
|