/***
* Coalevo Project
* http://www.coalevo.net
*
* (c) Verein zur Foerderung der Internetkommunikation, Austria
* http://www.vfi.or.at
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* You may obtain a copy of the License at:
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
***/
package net.coalevo.bot.bitly.impl;
import net.coalevo.bot.bitly.service.BitlyBotConfiguration;
import net.coalevo.bot.bitly.service.BitlyBotService;
import net.coalevo.foundation.model.*;
import net.coalevo.foundation.util.ConfigurationUpdateHandler;
import net.coalevo.foundation.util.LRUCacheMap;
import net.coalevo.foundation.util.metatype.MetaTypeDictionary;
import net.coalevo.foundation.util.metatype.MetaTypeDictionaryException;
import net.coalevo.messaging.model.*;
import net.coalevo.messaging.service.MessagingService;
import net.coalevo.presence.model.AgentIdentifierList;
import net.coalevo.presence.model.PresenceProxy;
import net.coalevo.presence.model.PresenceServiceListenerAdapter;
import net.coalevo.presence.model.ServicePresenceProxy;
import net.coalevo.security.model.ServiceAgentProxy;
import org.osgi.framework.BundleContext;
import org.slf4j.Marker;
import org.slf4j.MarkerFactory;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
/**
* This class implements the Bitly Bot as a Service.
*
*
* @author Dieter Wimberger (wimpi)
* @version @version@ (@date@)
*/
class BitlyBotServiceImpl
extends BaseService
implements BitlyBotService, ConfigurationUpdateHandler {
private Marker m_LogMarker = MarkerFactory.getMarker(BitlyBotServiceImpl.class.getName());
private Messages m_BundleMessages;
//Coalevo Services
private ServiceMediator m_Services;
//Security Context
private ServiceAgentProxy m_ServiceAgentProxy;
private ServicePresenceProxy m_ServicePresenceProxy;
private ServiceMessagingProxy m_ServiceMessagingProxy;
//Presence
private Set m_PresentSubscribers;
//Cache
private LRUCacheMap m_URLCache;
private LRUCacheMap m_InfoCache;
//Client
private BitlyClient m_BitlyClient;
BitlyBotServiceImpl() {
super(BitlyBotService.class.getName(), ACTIONS);
m_URLCache = new LRUCacheMap(20);
m_InfoCache = new LRUCacheMap(20);
m_BitlyClient = new BitlyClient();
}//constructor
public boolean activate(BundleContext bc) {
//1. Services and Messages
m_Services = Activator.getServices();
m_BundleMessages = Activator.getBundleMessages();
//3. Authenticate ourself
m_ServiceAgentProxy = new ServiceAgentProxy(this, Activator.log());
m_ServiceAgentProxy.activate(bc);
//4. Presence Handling
m_PresentSubscribers = new HashSet();
m_ServicePresenceProxy = new ServicePresenceProxy(
new PresenceListenerImpl(),
m_ServiceAgentProxy,
Activator.log());
m_ServicePresenceProxy.activate(bc);
//5. Messaging Handling
m_ServiceMessagingProxy = new ServiceMessagingProxy(
new MessagingServiceListenerImpl(),
m_ServicePresenceProxy,
Activator.log()
);
//3. Run Presence Subscription task
m_Services.getExecutionService(ServiceMediator.WAIT_UNLIMITED).execute(this.m_ServiceAgentProxy.getAuthenticPeer(), new Runnable() {
public void run() {
try {
AgentIdentifierList requests = m_ServicePresenceProxy.getPresence().getRequestedSubscriptions();
for (Iterator iterator = requests.iterator(); iterator.hasNext();) {
AgentIdentifier aid = iterator.next();
m_ServicePresenceProxy.getPresenceService().allowSubscription(m_ServicePresenceProxy.getPresence(), aid);
m_ServicePresenceProxy.getPresenceService().requestSubscriptionTo(m_ServicePresenceProxy.getPresence(), aid);
}
} catch (Exception ex) {
Activator.log().error(m_LogMarker, "activate()", ex);
}
}//run
});
m_ServiceMessagingProxy.activate(bc);
//2. Handle configuration
update(m_Services.getConfigMediator().getConfiguration());
m_Services.getConfigMediator().addUpdateHandler(this);
return true;
}//activate
public boolean deactivate() {
if (m_ServiceMessagingProxy != null) {
m_ServiceMessagingProxy.deactivate();
m_ServiceMessagingProxy = null;
}
if (m_ServicePresenceProxy != null) {
m_ServicePresenceProxy.deactivate();
m_ServicePresenceProxy = null;
}
if (m_ServiceAgentProxy != null) {
m_ServiceAgentProxy.deactivate();
m_ServiceAgentProxy = null;
}
if (m_URLCache != null) {
m_URLCache.clear();
}
if (m_InfoCache != null) {
m_InfoCache.clear();
}
m_Services = null;
return true;
}//deactivate
public void update(MetaTypeDictionary mtd) {
//1. Configure from persistent configuration
int urlcsize = 20;
int infocsize = 20;
String login = "";
String apikey = "";
//Retrieve config
try {
login = mtd.getString(BitlyBotConfiguration.BITLY_LOGIN_KEY);
} catch (MetaTypeDictionaryException ex) {
Activator.log().error(m_LogMarker,
m_BundleMessages.get("BitlyBotServiceImpl.activation.configexception", "attribute", BitlyBotConfiguration.BITLY_LOGIN_KEY),
ex
);
}
try {
apikey = mtd.getString(BitlyBotConfiguration.BITLY_APIKEY_KEY);
} catch (MetaTypeDictionaryException ex) {
Activator.log().error(m_LogMarker,
m_BundleMessages.get("BitlyBotServiceImpl.activation.configexception", "attribute", BitlyBotConfiguration.BITLY_APIKEY_KEY),
ex
);
}
try {
urlcsize = mtd.getInteger(BitlyBotConfiguration.BITLY_URLCACHESIZE_KEY).intValue();
} catch (MetaTypeDictionaryException ex) {
Activator.log().error(m_LogMarker,
m_BundleMessages.get("BitlyBotServiceImpl.activation.configexception", "attribute", BitlyBotConfiguration.BITLY_URLCACHESIZE_KEY),
ex
);
}
try {
infocsize = mtd.getInteger(BitlyBotConfiguration.BITLY_URLCACHESIZE_KEY).intValue();
} catch (MetaTypeDictionaryException ex) {
Activator.log().error(m_LogMarker,
m_BundleMessages.get("BitlyBotServiceImpl.activation.configexception", "attribute", BitlyBotConfiguration.BITLY_URLCACHESIZE_KEY),
ex
);
}
//Update config
m_BitlyClient.setAuthentication(login, apikey);
//Update cache setting
m_URLCache.setCeiling(urlcsize);
m_InfoCache.setCeiling(infocsize);
}//update
private void sendMessage(AgentIdentifier to, String content, String format) {
try {
MessagingService ms = m_ServiceMessagingProxy.getMessagingService();
EditableInteractiveMessage im = ms.create(
m_ServicePresenceProxy.getPresence(),
InteractiveMessageTypes.CHAT,
m_Services.getUUIDGeneratorService(ServiceMediator.NO_WAIT).getUID()
);
im.setTo(to);
im.setBody(content);
im.setConfirmationRequired(false);
if (format != null && format.length() > 0) {
im.setFormattingHint(format);
}
ms.send(m_ServicePresenceProxy.getPresence(), im);
} catch (Exception mex) {
Activator.log().error(m_LogMarker, "sendMessage()", mex);
}
}//sendMessage
//** Command Implementations **//
private void doHelp(AgentIdentifier aid) {
sendMessage(aid, m_BundleMessages.get("BitlyBotServiceImpl.bot.help"), FORMAT_TML);
}//doHelp
private void doShorten(AgentIdentifier aid, String url) {
String msg;
try {
BitlyURL bu = m_BitlyClient.shorten(url);
if (bu.notError()) {
msg = m_BundleMessages.get("BitlyBotServiceImpl.bot.shortened", "long", bu.getLong(), "short", bu.getShort());
} else {
msg = m_BundleMessages.get(
"BitlyBotServiceImpl.bot.bitlyerror",
"code", Integer.toString(bu.getError()),
"msg", bu.getErrorMessage()
);
}
} catch (MalformedURLException muex) {
msg = m_BundleMessages.get("BitlyBotServiceImpl.bot.malformedurl");
} catch (IOException ex) {
msg = m_BundleMessages.get("BitlyBotServiceImpl.bot.iofailed");
}
sendMessage(aid, msg, FORMAT_TML);
}//doShorten
private void doExpand(AgentIdentifier aid, String str) {
String msg;
BitlyURL bu = null;
try {
//check cache
if (str.startsWith("http://")) {
String hash = str.substring(str.lastIndexOf('/') + 1);
bu = m_URLCache.get(hash);
}
if (bu == null) {
bu = m_BitlyClient.expand(str);
}
if (bu.notError()) {
m_URLCache.put(bu.getHash(),bu);
msg = m_BundleMessages.get("BitlyBotServiceImpl.bot.expanded", "long", bu.getLong(), "short", bu.getShort());
} else {
msg = m_BundleMessages.get(
"BitlyBotServiceImpl.bot.bitlyerror",
"code", Integer.toString(bu.getError()),
"msg", bu.getErrorMessage()
);
}
} catch (MalformedURLException muex) {
msg = m_BundleMessages.get("BitlyBotServiceImpl.bot.malformedurl");
} catch (IOException ex) {
msg = m_BundleMessages.get("BitlyBotServiceImpl.bot.iofailed");
}
sendMessage(aid, msg, FORMAT_TML);
}//doExpand
private void doInfo(AgentIdentifier aid, String str) {
String msg;
BitlyInfo bi = null;
try {
//check cache
if (str.startsWith("http://")) {
String hash = str.substring(str.lastIndexOf('/') + 1);
bi = m_InfoCache.get(hash);
}
if (bi == null) {
bi = m_BitlyClient.info(str);
}
if (bi.notError()) {
m_InfoCache.put(bi.getHash(), bi);
MessageAttributes attr = m_BundleMessages.leaseAttributes();
attr.add("hash", bi.getHash());
addIf(attr, "userhash", bi.getUserHash());
attr.add("short", bi.getShort());
attr.add("long", bi.getLong());
attr.add("longenc", BitlyClient.encodeURL(bi.getLong()));
addIf(attr, "title", bi.getHTMLTitle());
addIf(attr, "desc", bi.getHTMLMetaDescription());
addIf(attr, "keyword", bi.getKeyword());
addIf(attr, "keywords", bi.getHTMLMetaKeywords().toString());
addIf(attr, "shortenuser", bi.getShortenUser());
msg = m_BundleMessages.get("BitlyBotServiceImpl.bot.urlinfo", attr);
} else {
msg = m_BundleMessages.get(
"BitlyBotServiceImpl.bot.bitlyerror",
"code", Integer.toString(bi.getError()),
"msg", bi.getErrorMessage()
);
}
} catch (MalformedURLException muex) {
msg = m_BundleMessages.get("BitlyBotServiceImpl.bot.malformedurl");
} catch (IOException ex) {
msg = m_BundleMessages.get("BitlyBotServiceImpl.bot.iofailed");
}
sendMessage(aid, msg, FORMAT_TML);
}//doInfo
private void addIf(MessageAttributes attr, String key, String val) {
if(val != null || val.trim().length() > 0) {
attr.add(key, val);
}
}//addIf
private void doStats(AgentIdentifier aid, String str) {
String msg;
BitlyStats bs = null;
try {
bs = m_BitlyClient.stats(str);
if (bs.notError()) {
MessageAttributes attr = m_BundleMessages.leaseAttributes();
attr.add("hash", bs.getHash());
attr.add("short", bs.getShort());
attr.add("clicks", Long.toString(bs.getClicks()));
msg = m_BundleMessages.get("BitlyBotServiceImpl.bot.urlstats", attr);
} else {
msg = m_BundleMessages.get(
"BitlyBotServiceImpl.bot.bitlyerror",
"code", Integer.toString(bs.getError()),
"msg", bs.getErrorMessage()
);
}
} catch (MalformedURLException muex) {
msg = m_BundleMessages.get("BitlyBotServiceImpl.bot.malformedurl");
} catch (IOException ex) {
msg = m_BundleMessages.get("BitlyBotServiceImpl.bot.iofailed");
}
sendMessage(aid, msg, FORMAT_TML);
}//doStats
class PresenceListenerImpl
extends PresenceServiceListenerAdapter {
public void found(PresenceProxy p) {
if (p.isAvailable()) {
handlePresence(p.getAgentIdentifier());
} else {
handleAbsence(p.getAgentIdentifier());
}
}//found
public void receivedPresence(PresenceProxy p) {
if (p.isAvailable()) {
handlePresence(p.getAgentIdentifier());
} else {
handleAbsence(p.getAgentIdentifier());
}
}//receivedPresence
public void becamePresent(PresenceProxy p) {
handlePresence(p.getAgentIdentifier());
}//becamePresent
public void becameAbsent(PresenceProxy p) {
handleAbsence(p.getAgentIdentifier());
}//becameAbsent
public void statusUpdated(PresenceProxy p) {
if (p.isAvailable()) {
handlePresence(p.getAgentIdentifier());
} else {
handleAbsence(p.getAgentIdentifier());
}
}//statusUpdated
public void subscriptionCanceled(final AgentIdentifier aid) {
// unregister(aid);
}//subscriptionCanceled
public void subscriptionAllowed(final AgentIdentifier aid) {
m_Services.getExecutionService(ServiceMediator.NO_WAIT).execute(BitlyBotServiceImpl.this.m_ServiceAgentProxy.getAuthenticPeer(), new Runnable() {
public void run() {
m_ServicePresenceProxy.getPresenceService().requestSubscriptionTo(
m_ServicePresenceProxy.getPresence(),
aid
);
}//run
}//runnable
);//execute
}//subscriptionAllowed
public void requestedSubscription(PresenceProxy p) {
final AgentIdentifier aid = p.getAgentIdentifier();
m_Services.getExecutionService(ServiceMediator.NO_WAIT).execute(BitlyBotServiceImpl.this.m_ServiceAgentProxy.getAuthenticPeer(), new Runnable() {
public void run() {
m_ServicePresenceProxy.getPresenceService().allowSubscription(
m_ServicePresenceProxy.getPresence(),
aid
);
m_ServicePresenceProxy.getPresenceService().requestSubscriptionTo(
m_ServicePresenceProxy.getPresence(),
aid
);
}//run
}//runnable
);//execute
}//requestedSubscription
protected void handlePresence(final AgentIdentifier aid) {
m_Services.getExecutionService(ServiceMediator.NO_WAIT).execute(BitlyBotServiceImpl.this.m_ServiceAgentProxy.getAuthenticPeer(), new Runnable() {
public void run() {
//subcriber logic
if (m_PresentSubscribers.contains(aid)) {
return;
} else {
m_PresentSubscribers.add(aid);
}
}//run
}//runnable
);//execute
}//handlePresence
protected void handleAbsence(final AgentIdentifier aid) {
m_Services.getExecutionService(ServiceMediator.NO_WAIT).execute(BitlyBotServiceImpl.this.m_ServiceAgentProxy.getAuthenticPeer(), new Runnable() {
public void run() {
if (!m_PresentSubscribers.remove(aid)) {
return;
}
}//run
}//runnable
);//execute
}//handleAbsence
}//PresenceListenerImpl
class MessagingServiceListenerImpl
extends MessagingServiceListenerAdapter {
public boolean received(final InteractiveMessage m) {
m_Services.getExecutionService(ServiceMediator.NO_WAIT).execute(BitlyBotServiceImpl.this.m_ServiceAgentProxy.getAuthenticPeer(), new Runnable() {
public void run() {
EventInfo evinf = m.getEventInfo();
//1. Confirmation
if (m.isConfirmationRequired()) {
try {
m_ServiceMessagingProxy.getMessagingService().confirm(m_ServicePresenceProxy.getPresence(),
evinf
);
} catch (MessagingException ex) {
Activator.log().error(m_LogMarker, "MessagingServiceListener:received()", ex);
}
}
//2. Skip Broadcasts
if(m.isBroadcast()) {
return;
}
//3. Interpret content
String str = m.getBody();
if (str != null && str.length() > 0) {
if (str.startsWith(BITLY_CMD_HELP)) {
doHelp(m.getFrom());
} else if (str.startsWith(BITLY_CMD_SHORTEN)) {
doShorten(m.getFrom(), str.substring(BITLY_CMD_SHORTEN.length()).trim());
} else if (str.startsWith(BITLY_CMD_EXPAND)) {
doExpand(m.getFrom(), str.substring(BITLY_CMD_EXPAND.length()).trim());
} else if (str.startsWith(BITLY_URL)) {
doInfo(m.getFrom(),str.trim());
} else if (str.startsWith(ANY_URL)) {
doShorten(m.getFrom(), str.trim());
} else if (str.startsWith(BITLY_CMD_INFO)) {
doInfo(m.getFrom(), str.substring(BITLY_CMD_INFO.length()).trim());
} else if (str.startsWith(BITLY_CMD_STATS)) {
doStats(m.getFrom(), str.substring(BITLY_CMD_STATS.length()).trim());
} else {
sendMessage(m.getFrom(), m_BundleMessages.get("BitlyBotServiceImpl.bot.nosuchcmd", "cmd", str.trim()), FORMAT_TML);
}
}
}//run
}//runnable
);//execute
return true;
}//received
}//MessagingServiceListenerImpl
private static Action[] ACTIONS = {};
private static final String BITLY_CMD_HELP = "help";
private static final String BITLY_CMD_SHORTEN = "shorten";
private static final String BITLY_CMD_EXPAND = "expand";
private static final String BITLY_CMD_INFO = "info";
private static final String BITLY_CMD_STATS = "stats";
private static final String BITLY_URL = "http://bit.ly";
private static final String ANY_URL = "http://";
private static final String FORMAT_TML = "tml";
}//class BitlyBotServiceImpl