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.plugin.twitter;
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.Entry;
036: import org.blojsom.blog.User;
037: import org.blojsom.event.Event;
038: import org.blojsom.event.EventBroadcaster;
039: import org.blojsom.event.Listener;
040: import org.blojsom.plugin.Plugin;
041: import org.blojsom.plugin.PluginException;
042: import org.blojsom.plugin.admin.event.EntryEvent;
043: import org.blojsom.plugin.admin.event.EntryAddedEvent;
044: import org.blojsom.plugin.admin.event.EntryUpdatedEvent;
045: import org.blojsom.util.BlojsomUtils;
046: import org.blojsom.fetcher.Fetcher;
047: import org.blojsom.fetcher.FetcherException;
048:
049: import javax.servlet.http.HttpServletRequest;
050: import javax.servlet.http.HttpServletResponse;
051: import java.util.Map;
052: import java.text.MessageFormat;
053: import java.net.Authenticator;
054: import java.net.PasswordAuthentication;
055: import java.net.URL;
056: import java.net.URLConnection;
057: import java.io.BufferedReader;
058: import java.io.InputStreamReader;
059: import java.io.IOException;
060: import java.io.OutputStreamWriter;
061:
062: /**
063: * Twitter notification plugin for the <a href="http://twitter.com/">Twitter</a> service
064: *
065: * @author David Czarnecki
066: * @since blojsom 3.1
067: * @version $Id: TwitterNotificationPlugin.java,v 1.5 2007/01/17 02:35:15 czarneckid Exp $
068: */
069: public class TwitterNotificationPlugin implements Plugin, Listener {
070:
071: private Log _logger = LogFactory
072: .getLog(TwitterNotificationPlugin.class);
073:
074: private static final String TWITTER_STATUS_UPDATE_URL = "http://twitter.com/statuses/update.xml";
075: private static final String TWITTER_STATUS_PARAMETER = "status";
076: private static final String TWITTER_DEFAULT_STATUS_UPDATE_TEXT = "Currently blogging {0}";
077:
078: private static final String TWITTER_SIGN_IN_IP = "plugin-twitter-sign-in";
079: private static final String TWITTER_PASSWORD_IP = "plugin-twitter-password";
080: private static final String TWITTER_UPDATE_ON_ENTRY_ADDED_IP = "plugin-twitter-update-on-entry-added";
081: private static final String TWITTER_UPDATE_ON_ENTRY_UPDATED_IP = "plugin-twitter-update-on-entry-updated";
082: private static final String TWITTER_STATUS_UPDATE_TEXT_IP = "plugin-twitter-update-text";
083:
084: private EventBroadcaster _eventBroadcaster;
085: private Fetcher _fetcher;
086:
087: private String _twitterUpdateURL = TWITTER_STATUS_UPDATE_URL;
088:
089: /**
090: * Create a new instance of the Twitter notification plugin
091: */
092: public TwitterNotificationPlugin() {
093: }
094:
095: /**
096: * Set the {@link EventBroadcaster}
097: *
098: * @param eventBroadcaster {@link EventBroadcaster}
099: */
100: public void setEventBroadcaster(EventBroadcaster eventBroadcaster) {
101: _eventBroadcaster = eventBroadcaster;
102: }
103:
104: /**
105: * Set the {@link Fetcher}
106: *
107: * @param fetcher {@link Fetcher}
108: */
109: public void setFetcher(Fetcher fetcher) {
110: _fetcher = fetcher;
111: }
112:
113: /**
114: * Set the Twitter update URL
115: *
116: * @param updateURL Twitter update URL
117: */
118: public void setTwitterStatusUpdateURL(String updateURL) {
119: _twitterUpdateURL = updateURL;
120: }
121:
122: /**
123: * Initialize this plugin. This method only called when the plugin is instantiated.
124: *
125: * @throws PluginException If there is an error initializing the plugin
126: */
127: public void init() throws PluginException {
128: _eventBroadcaster.addListener(this );
129:
130: if (_logger.isDebugEnabled()) {
131: _logger.debug("Initialized Twitter notification plugin");
132: }
133: }
134:
135: /**
136: * Process the blog entries
137: *
138: * @param httpServletRequest Request
139: * @param httpServletResponse Response
140: * @param blog {@link Blog} instance
141: * @param context Context
142: * @param entries Blog entries retrieved for the particular request
143: * @return Modified set of blog entries
144: * @throws PluginException If there is an error processing the blog entries
145: */
146: public Entry[] process(HttpServletRequest httpServletRequest,
147: HttpServletResponse httpServletResponse, Blog blog,
148: Map context, Entry[] entries) throws PluginException {
149: return entries;
150: }
151:
152: /**
153: * Perform any cleanup for the plugin. Called after {@link #process}.
154: *
155: * @throws PluginException If there is an error performing cleanup for this plugin
156: */
157: public void cleanup() throws PluginException {
158: }
159:
160: /**
161: * Called when BlojsomServlet is taken out of service
162: *
163: * @throws PluginException If there is an error in finalizing this plugin
164: */
165: public void destroy() throws PluginException {
166: }
167:
168: /**
169: * Handle an event broadcast from another component
170: *
171: * @param event {@link Event} to be handled
172: */
173: public void handleEvent(Event event) {
174: if (!BlojsomUtils.checkNullOrBlank(_twitterUpdateURL)) {
175: if ((event instanceof EntryAddedEvent)
176: || (event instanceof EntryUpdatedEvent)) {
177: EntryEvent entryEvent = (EntryEvent) event;
178:
179: Blog blog = entryEvent.getBlog();
180: String author = entryEvent.getEntry().getAuthor();
181: User user;
182:
183: try {
184: user = _fetcher.loadUser(blog, author);
185: } catch (FetcherException e) {
186: if (_logger.isErrorEnabled()) {
187: _logger
188: .error(
189: "Error loading User object to retrieve Twitter properties",
190: e);
191: }
192:
193: return;
194: }
195:
196: if (!BlojsomUtils.checkNullOrBlank((String) user
197: .getMetaData().get(TWITTER_SIGN_IN_IP))
198: && !BlojsomUtils
199: .checkNullOrBlank((String) user
200: .getMetaData().get(
201: TWITTER_PASSWORD_IP))) {
202:
203: String signIn = (String) user.getMetaData().get(
204: TWITTER_SIGN_IN_IP);
205: String password = (String) user.getMetaData().get(
206: TWITTER_PASSWORD_IP);
207: String updateText = (String) user.getMetaData()
208: .get(TWITTER_STATUS_UPDATE_TEXT_IP);
209:
210: if (BlojsomUtils.checkNullOrBlank(updateText)) {
211: updateText = TWITTER_DEFAULT_STATUS_UPDATE_TEXT;
212: }
213:
214: if (("true".equals(user.getMetaData().get(
215: TWITTER_UPDATE_ON_ENTRY_ADDED_IP)))
216: || ("true"
217: .equals(user
218: .getMetaData()
219: .get(
220: TWITTER_UPDATE_ON_ENTRY_UPDATED_IP)))) {
221: String title = entryEvent.getEntry().getTitle();
222: String twitterUpdate = BlojsomUtils
223: .urlEncode(BlojsomUtils
224: .escapeString(MessageFormat
225: .format(
226: updateText,
227: new Object[] { title })));
228:
229: Authenticator
230: .setDefault(new TwitterAuthenticator(
231: signIn, password));
232:
233: try {
234: URL url = new URL(_twitterUpdateURL);
235: URLConnection urlConnection = url
236: .openConnection();
237: urlConnection.setUseCaches(false);
238: urlConnection.setDoInput(true);
239: urlConnection.setDoOutput(true);
240: urlConnection
241: .setRequestProperty("Content-Type",
242: "application/x-www-form-urlencoded");
243:
244: String twitterData = TWITTER_STATUS_PARAMETER
245: + "=" + twitterUpdate;
246: OutputStreamWriter twitterWriter = new OutputStreamWriter(
247: urlConnection.getOutputStream());
248: twitterWriter.write(twitterData);
249: twitterWriter.flush();
250: twitterWriter.close();
251:
252: // Read all the text returned by the server
253: BufferedReader twitterReader = new BufferedReader(
254: new InputStreamReader(urlConnection
255: .getInputStream()));
256: StringBuffer twitterReply = new StringBuffer();
257: String input;
258:
259: while ((input = twitterReader.readLine()) != null) {
260: twitterReply.append(input);
261: }
262:
263: twitterReader.close();
264:
265: if (BlojsomUtils
266: .checkNullOrBlank(twitterReply
267: .toString())) {
268: if (_logger.isErrorEnabled()) {
269: _logger
270: .error("Error communicating update to Twitter");
271: }
272: } else {
273: if (_logger.isDebugEnabled()) {
274: _logger
275: .debug("Successfully sent update to Twitter");
276: }
277: }
278: } catch (IOException e) {
279: if (_logger.isErrorEnabled()) {
280: _logger.error(e);
281: }
282: }
283: } else {
284: if (_logger.isDebugEnabled()) {
285: _logger
286: .debug("Twitter notification update not enabled for either add or update entry events");
287: }
288: }
289: } else {
290: if (_logger.isDebugEnabled()) {
291: _logger
292: .debug("Twitter sign in and/or password is null or blank");
293: }
294: }
295: }
296: }
297: }
298:
299: /**
300: * Process an event from another component
301: *
302: * @param event {@link Event} to be handled
303: */
304: public void processEvent(Event event) {
305: }
306:
307: public class TwitterAuthenticator extends Authenticator {
308:
309: private String _username;
310: private String _password;
311:
312: /**
313: * Create a new instance of the Twitter authenticator
314: *
315: * @param username Twitter username
316: * @param password Twitter password
317: */
318: public TwitterAuthenticator(String username, String password) {
319: _username = username;
320: _password = password;
321:
322: }
323:
324: /**
325: * Return a {@link PasswordAuthentication} with the username and password for Twitter
326: *
327: * @return {@link PasswordAuthentication} with the username and password for Twitter
328: */
329: protected PasswordAuthentication getPasswordAuthentication() {
330: // Return the information
331: return new PasswordAuthentication(_username, _password
332: .toCharArray());
333: }
334: }
335: }
|