I have no special talent. I'm only passionately curios - Albert Einstein
Automate your Twitter Feed in Java Comment on Automate your Twitter Feed in Java 4

  I set out to automate the Twitter feed for the social networking site I developed, MyBuckStory.com.  The site is written in Java, and I wanted to continue using bit.ly to shorten my URLs, and the custom format I had come up with to use as a Twitter status update for new stories posted on the site.

  I was pleasantly surprised to find an open source library out there already, called Twitter4J.  The library was quick and simple to get myself up and running.  The closest thing I could find for a Java library for bit.ly was a JSP tag library.  I wanted to be able to run this in the service layer of my application, so I began developing my own custom solution.

  I've recently been developing webservices using Apache CXF, and have gotten used to the JaxB annotations which make working with XML a breeze.  I went ahead and sent a sample request to the bit.ly API for the 'shorten' function (a RESTful service API), and took a look at the response, and modeled my JaxB bean after that response.  Here's what I came up with:

 


Bitly.java


import java.util.List;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name="bitly")
@XmlAccessorType(XmlAccessType.FIELD)
public class Bitly{

@XmlElement(name = "errorCode")
private String errorCode;

@XmlElement(name = "errorMessage")
private String errorMessage;

@XmlElement(name = "statusCode")
private String statusCode;

@XmlElementWrapper(name="results")
@XmlElement(name="nodeKeyVal",type=NodeKeyVal.class)
private final List results;


public Bitly(){
results = null;
}

public String getErrorCode(){
return errorCode;
}

public String getErrorMessage(){
return errorMessage;
}

public String getStatusCode(){
return statusCode;
}

public List getResults(){
return results;
}
}

NodeKeyVal.java


import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name="nodeKeyVal")
@XmlAccessorType(XmlAccessType.FIELD)
public class NodeKeyVal{

@XmlElement(name = "userHash")
private String userHash;

@XmlElement(name = "shortKeywordUrl")
private String shortKeywordUrl;

@XmlElement(name = "hash")
private String hash;

@XmlElement(name = "nodeKey")
private String nodeKey;

@XmlElement(name = "shortUrl")
private String shortUrl;

public String getUserHash(){
return userHash;
}
public String getShortKeywordUrl(){
return shortKeywordUrl;
}
public String getHash(){
return hash;
}
public String getNodeKey(){
return nodeKey;
}
public String getShortUrl(){
return shortUrl;
}
}

BitlyClient.java - assumes XML format



import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;

import javax.xml.bind.JAXBException;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

import model.bitly.Bitly;
import util.JAXBUtil;

public class BitlyClient {

private static final Logger logger = Logger.getLogger(BitlyClient.class);
private static final String PARAM_VERSION = "version";
private static final String PARAM_FORMAT = "format";
private static final String PARAM_LOGIN = "login";
private static final String PARAM_API_KEY = "apiKey";
private static final String PARAM_LONG_URL = "longUrl";

private String version;
private String login;
private String apiKey;
private String shortenApiUrl;


public String shorten(String urlToShorten){
String shortenedUrl = "";
InputStream is = null;
try {
StringBuffer buffer = new StringBuffer(shortenApiUrl)
.append("?").append(PARAM_VERSION).append("=").append(version)
.append("&").append(PARAM_API_KEY).append("=").append(apiKey)
.append("&").append(PARAM_LOGIN).append("=").append(login)
.append("&").append(PARAM_FORMAT).append("=").append("xml")
.append("&").append(PARAM_LONG_URL).append("=").append(urlToShorten);

String url = buffer.toString();
logger.debug("Calling bit.ly shorten API URL: " + url);

URL bitlyUrl = new URL(url);

is = bitlyUrl.openStream();

Bitly bitly = (Bitly)JAXBUtil.parseXML(new BufferedReader(new InputStreamReader(is)), Bitly.class);

shortenedUrl = bitly.getResults().get(0).getShortUrl();
} catch (MalformedURLException e) {
logger.error("Bad bit.ly URL", e);
}catch(IOException e){
logger.error("IOException when trying to call openStream bit.ly URL", e);
}catch(JAXBException e){
logger.error("JAXB error when reading bit.ly shorten response", e);
}finally{
IOUtils.closeQuietly(is);
}

return shortenedUrl;
}


public void setVersion(String version) {
this.version = version;
}

public void setLogin(String login) {
this.login = login;
}


public void setApiKey(String apiKey) {
this.apiKey = apiKey;
}


public void setShortenApiUrl(String shortenApiUrl) {
this.shortenApiUrl = shortenApiUrl;
}

}

TwitterClient.java - uses Twitter4j


import org.apache.log4j.Logger;
import org.htmlparser.util.ParserException;

import twitter4j.Twitter;
import twitter4j.TwitterException;

import model.Story;
import util.PreviewGenerator;

public class TwitterClient {

private static final Logger logger = Logger.getLogger(TwitterClient.class);
private static final String MAIN_URL = "http://mybuckstory.com";
private static final int STATUS_MAX_LENGTH = 140;
private Twitter twitter;
private BitlyClient bitly;

public void updateStatus(Story story) throws ParserException{
String fullUrl = MAIN_URL + story.getUri();
String bitlyUrl = bitly.shorten(fullUrl);

String prefix = story.getTitle() + " - ";
String suffix = "..." + bitlyUrl;

if(!story.getCategories().isEmpty()){
suffix += " #" + story.getCategories().iterator().next().getName();
}

int lengthSoFar = prefix.length() + suffix.length();
int lengthRemaining = STATUS_MAX_LENGTH - lengthSoFar;
//PreviewGenerator uses an HTMLParser to get the Text only content, removing HTML tags
String storyPreview = PreviewGenerator.generatePreview(story.getText(), lengthRemaining);

String status = prefix + storyPreview + suffix;

try {
twitter.updateStatus(status);
} catch (TwitterException e) {
logger.warn("Error occurred when updating Twitter status with new Story", e);
}
}

public void setTwitter(Twitter twitter) {
this.twitter = twitter;
}

public void setBitlyClient(BitlyClient bitly) {
this.bitly = bitly;
}
}

JAXBUtil.java - this code was taken from here


import java.io.Reader;
import java.io.Writer;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.ValidationEvent;
import javax.xml.bind.ValidationEventHandler;
import javax.xml.bind.ValidationEventLocator;

import org.w3c.dom.Node;

/**
* Tools for working with the JAXB (XML Binding) library.
*/
public class JAXBUtil{

/**
* Parse the XML supplied by the reader into the corresponding tree of Java
* objects.
* @param
*
* @param reader
* Cannot be null. The source of the XML.
* @param rootElementClass
* Cannot be null. The type of the root element.
* @return the Java object that is the root of the tree, of type
* rootElement.
* @throws JAXBException
* if an error occurs parsing the XML.
*/
@SuppressWarnings("unchecked")
public static Object parseXML(Reader reader, Class rootElementClass) throws JAXBException{

if(rootElementClass == null)
throw new IllegalArgumentException("rootElementClass is null");
if(reader == null)
throw new IllegalArgumentException("reader is null");

JAXBContext context = JAXBContext.newInstance(rootElementClass);
Unmarshaller unmarshaller = context.createUnmarshaller();

CollectingValidationEventHandler handler = new CollectingValidationEventHandler();
unmarshaller.setEventHandler(handler);

Object object = unmarshaller.unmarshal(reader);
if(!handler.getMessages().isEmpty()){
String errorMessage = "XML parse errors:";
for(String message : handler.getMessages()){
errorMessage += "\n" + message;
}
throw new JAXBException(errorMessage);
}

return object;
}

/**
* Generate XML using the supplied root element as the root of the object
* tree and write the resulting XML to the specified writer
*
* @param rootElement
* Cannot be null.
* @param writer
* Cannot be null.
* @throws JAXBException
*/
public static void generateXML(Object rootElement, Writer writer) throws JAXBException{

if(rootElement == null)
throw new IllegalArgumentException("rootElement is null");
if(writer == null)
throw new IllegalArgumentException("writer is null");

JAXBContext context = JAXBContext.newInstance(rootElement.getClass());
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(rootElement, writer);
}

private static class CollectingValidationEventHandler implements ValidationEventHandler{
private List messages = new ArrayList();

public List getMessages(){
return messages;
}

public boolean handleEvent(ValidationEvent event){
if(event == null)
throw new IllegalArgumentException("event is null");

// calculate the severity prefix and return value
String severity = null;
boolean continueParsing = false;
switch(event.getSeverity()){
case ValidationEvent.WARNING:
severity = "Warning";
continueParsing = true; // continue after warnings
break;
case ValidationEvent.ERROR:
severity = "Error";
continueParsing = true; // terminate after errors
break;
case ValidationEvent.FATAL_ERROR:
severity = "Fatal error";
continueParsing = false; // terminate after fatal errors
break;
default:
assert false : "Unknown severity.";
}

String location = getLocationDescription(event);
String message = severity + " parsing " + location + " due to " + event.getMessage();
messages.add(message);

return continueParsing;
}

private String getLocationDescription(ValidationEvent event){
ValidationEventLocator locator = event.getLocator();
if(locator == null){
return "XML with location unavailable";
}

StringBuffer msg = new StringBuffer();
URL url = locator.getURL();
Object obj = locator.getObject();
Node node = locator.getNode();
int line = locator.getLineNumber();

if(url != null || line != -1){
msg.append("line " + line);
if(url != null)
msg.append(" of " + url);
}else if(obj != null){
msg.append(" obj: " + obj.toString());
}else if(node != null){
msg.append(" node: " + node.toString());
}

return msg.toString();
}
}
}


4 comments

SurfinGuy290 - March 07, 2010

Well my girl friend just broke up with me over email about an hour ago so thought this would be a great place to start spreding her pics like she did her legs. Just go to http://www.gf4free.com/members/surferdude23/ Enjoy! http://www.gf4free.com/members/surferdude23/uploads/9.jpg

irregarequawl - March 09, 2010

Greetings contact center A contact center (also referred to as a customer interaction center or e-contact center) is a central point in an enterprise from which all customer contacts are managed. The contact center typically includes one or more online call centers but may include other types of customer contact as well, including e-mail newsletters, postal mail catalogs, Web site inquiries and chats, and the collection of information from customers during in-store purchasing. A contact center is generally part of an enterprise's overall customer relationship management (CRM). A contact center would typically be provided with special software that would allow contact information to be routed to appropriate people, contacts to be tracked, and data to be gathered. A contact center is considered to be an important element in multichannel marketing. call center A call center is a physical place where customer and other telephone calls are handled by an organization, usually with some amount of computer automation. Typically, a call center has the ability to handle a considerable volume of calls at the same time, to screen calls and forward them to someone qualified to handle them, and to log calls. Call centers are used by mail-order catalog organizations, telemarketing companies, computer product help desks, and any large organization that uses the telephone to sell or service products and services. Two related terms are virtual call center and contact center. Call4Peace Worldwide was founded in 2007 and is a premier global provider of contact center and business process outsourcing solutions. With over 12 production and sales centers all around the world, Call4Peace Worldwide serves as a contact service company offering the very highest quality services, with Great Prices. Call4Peace Worldwide is a leading provider of innovative human resource products and services created to assist call centers improve their revenues, profits and other key operating metrics through improved talent acquisition & deployment; talent development; talent benchmarking & certification; and talent affirmation & differentiation. Call4Peace Worldwide has been a global leader in Contact Center solutions for over 14 years, our solutions are used by 30 companies in 11 countries around the world, processing millions of calls each and every day. Call4Peace Worldwide Contact Center solutions are highly reliable, easy to use, and provide multiple customer configurable applications on a single platform. Standard applications include: Call Processing, Auto Attendant, Basic ACD Announcements and customizable Music-on-Hold. Many customers have selected Call4Peace Worldwide to help resolve two important Contact Center challenges: Specialized verticals include financial services, healthcare, insurance, telecommunications, energy, publishing and political advocacy/voter contact. Contact: info@call4peace.com call us # 1.917.484.6430 http://www.zimbio.com/callcenter11/articles/Ynp7XKgbfpd/WWW+CALL4PEACE+COM+B2B+LEADS+MARKETING+EXPERTS Tel Aviv Telemarketing equipment

Engennanedy - March 08, 2010

http://zimmerinteractive.com/childrensmiracle/wp-content/uploads/2008/09/childrens-hospital-image.jpg Large companies and private folk are donating to help others. Are you doing anything to help? Try to Donate Something - Your time, old clothes, or money. http://salvationarmy.org http://www.redcross.org/ http://www.amnesty.org/

SurfinGuy800 - March 09, 2010

Well my girl friend just broke up with me over email about an hour ago so thought this would be a great place to start spreding her pics like she did her legs. Just go to http://www.gf4free.com/members/surferdude23/ Enjoy! http://www.gf4free.com/members/surferdude23/uploads/9.jpg

Add a comment

Please provide your name, email address (won't be published) and a comment

About

David Malone is a Java developer residing in the Twin Cities area.  He has been developing enterprise applications since 2004.  This is his personal blog, as well as his design and development workspace.