package org.springframework.roo.shell.jline.osgi;
import static org.springframework.roo.support.util.AnsiEscapeCode.FG_CYAN;
import static org.springframework.roo.support.util.AnsiEscapeCode.FG_GREEN;
import static org.springframework.roo.support.util.AnsiEscapeCode.FG_MAGENTA;
import static org.springframework.roo.support.util.AnsiEscapeCode.FG_YELLOW;
import static org.springframework.roo.support.util.AnsiEscapeCode.REVERSE;
import static org.springframework.roo.support.util.AnsiEscapeCode.UNDERSCORE;
import static org.springframework.roo.support.util.AnsiEscapeCode.decorate;
import java.io.InputStream;
import java.net.URL;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Random;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.SystemUtils;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
import org.osgi.service.component.ComponentContext;
import org.springframework.roo.shell.ExecutionStrategy;
import org.springframework.roo.shell.Parser;
import org.springframework.roo.shell.jline.JLineShell;
import org.springframework.roo.support.osgi.OSGiUtils;
import org.springframework.roo.url.stream.UrlInputStreamService;
/**
* OSGi component launcher for {@link JLineShell}.
*
* @author Ben Alex
* @since 1.1
*/
@Component(immediate = true)
@Service
public class JLineShellComponent extends JLineShell {
@Reference private ExecutionStrategy executionStrategy;
@Reference private Parser parser;
@Reference private UrlInputStreamService urlInputStreamService;
private ComponentContext context;
protected void activate(final ComponentContext context) {
this.context = context;
final Thread thread = new Thread(this, "Spring Roo JLine Shell");
thread.start();
}
protected void deactivate(final ComponentContext context) {
this.context = null;
closeShell();
}
@Override
protected Collection<URL> findResources(final String path) {
// For an OSGi bundle search, we add the root prefix to the given path
return OSGiUtils.findEntriesByPath(context.getBundleContext(),
OSGiUtils.ROOT_PATH + path);
}
@Override
protected ExecutionStrategy getExecutionStrategy() {
return executionStrategy;
}
private String getLatestFavouriteTweet() {
// Access Twitter's REST API
final String string = sendGetRequest(
"http://api.twitter.com/1/favorites.json",
"id=SpringRoo&count=5");
if (StringUtils.isBlank(string)) {
return null;
}
// Parse the returned JSON. This is a once off operation so we can used
// JSONValue.parse without penalty
final JSONArray object = (JSONArray) JSONValue.parse(string);
if (object == null) {
return null;
}
int index = 0;
if (object.size() > 4) {
index = new Random().nextInt(5);
}
final JSONObject jsonObject = (JSONObject) object.get(index);
if (jsonObject == null) {
return null;
}
final String screenName = (String) ((JSONObject) jsonObject.get("user"))
.get("screen_name");
String tweet = (String) jsonObject.get("text");
// We only want one line
tweet = tweet.replace(IOUtils.LINE_SEPARATOR, " ");
final List<String> words = Arrays.asList(tweet.split(" "));
final StringBuilder sb = new StringBuilder();
// Add in Roo's twitter account to give context to the notification
sb.append(decorate("@" + screenName + ":",
SystemUtils.IS_OS_WINDOWS ? FG_YELLOW : REVERSE));
sb.append(" ");
// We want to colourise certain words. The codes used here should be
// moved to a ShellUtils and include a few helper methods
// This is a basic attempt at pattern identification, it should be
// adequate in most cases although may be incorrect for URLs.
// For example url.com/ttym: is valid by may mean "url.com/ttym" + ":"
for (final String word : words) {
if (word.startsWith("http://") || word.startsWith("https://")) {
// It's a URL
if (SystemUtils.IS_OS_WINDOWS) {
sb.append(decorate(word, FG_GREEN));
}
else {
sb.append(decorate(word, FG_GREEN, UNDERSCORE));
}
}
else if (word.charAt(0) == '@') {
// It's a Twitter username
sb.append(decorate(word, FG_MAGENTA));
}
else if (word.charAt(0) == '#') {
// It's a Twitter hash tag
sb.append(decorate(word, FG_CYAN));
}
else {
// All else default
sb.append(word);
}
// Add back separator
sb.append(" ");
}
return sb.toString();
}
@Override
protected Parser getParser() {
return parser;
}
@Override
public String getStartupNotifications() {
try {
return getLatestFavouriteTweet();
}
catch (final Exception e) {
return null;
}
}
// TODO: This should probably be moved to a HTTP service of some sort - JTT
// 29/08/11
private String sendGetRequest(final String endpoint,
final String requestParameters) {
if (!(endpoint.startsWith("http://") || endpoint.startsWith("https://"))) {
return null;
}
// Send a GET request to the servlet
InputStream inputStream = null;
try {
// Send data
String urlStr = endpoint;
if (StringUtils.isNotBlank(requestParameters)) {
urlStr += "?" + requestParameters;
}
// Get the response
final URL url = new URL(urlStr);
inputStream = urlInputStreamService.openConnection(url);
return IOUtils.toString(inputStream);
}
catch (final Exception e) {
return null;
}
finally {
IOUtils.closeQuietly(inputStream);
}
}
}