Google Search Plugin

Hello, I just finished writing a plugin that enables a user to search Google through a chat session. I wanted some other people to take a look at the code and give me some feedback.

package org.jivesoftware.messenger.plugin;

import org.jivesoftware.messenger.container.Plugin;

import org.jivesoftware.messenger.container.PluginManager;

import org.jivesoftware.messenger.*;

import org.xmpp.packet.Message;

import org.xmpp.packet.Packet;

import org.xmpp.packet.JID;

import org.xmpp.component.Component;

import org.xmpp.component.ComponentManager;

import org.xmpp.component.ComponentManagerFactory;

import java.io.File;

import java.util.*;

import com.google.soap.search.*;

public class GooglePlugin implements Plugin, Component {

private String apiKey;

private Integer maxResults;

private GoogleSearch search;

private String serviceName;

private SessionManager sessionManager;

private ComponentManager componentManager;

private PluginManager pluginManager;

public GooglePlugin() {

serviceName = “google”;

apiKey = “xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx”;

maxResults = 5;

}

public void initializePlugin(PluginManager manager, File pluginDirectory) {

pluginManager = manager;

sessionManager = SessionManager.getInstance();

componentManager = ComponentManagerFactory.getComponentManager();

try {

componentManager.addComponent(serviceName, this);

}

catch (Exception e) {

componentManager.getLog().error(e);

}

}

public void destroyPlugin() {

try {

componentManager.removeComponent(serviceName);

}

catch (Exception e) {

componentManager.getLog().error(e);

}

componentManager = null;

pluginManager = null;

sessionManager = null;

}

public void initialize(JID jid, ComponentManager componentManager) {

}

public void start() {

}

public void shutdown() {

}

public String getName() {

return pluginManager.getName(this);

}

public String getDescription() {

return pluginManager.getDescription(this);

}

public void processPacket(Packet packet) {

if (packet instanceof Message)

{

Message message = (Message)packet;

search = new GoogleSearch();

search.setKey(apiKey);

search.setQueryString(message.getBody());

search.setStartResult(0);

search.setMaxResults(maxResults);

search.setFilter(true);

search.setRestrict("");

search.setSafeSearch(false);

search.setLanguageRestricts(“lang_en”);

try

{

GoogleSearchResult searchResult = search.doSearch();

GoogleSearchResultElement[] resultElement = searchResult.getResultElements();

Integer searchCount = searchResult.getEstimatedTotalResultsCount();

String searchQuery = searchResult.getSearchQuery();

String newLine = System.getProperty(“line.separator”);

Message reply = new Message();

reply.setType(message.getType());

reply.setThread(message.getThread());

reply.setTo(message.getFrom());

reply.setFrom(message.getTo());

reply.setBody("Results 1 - " + maxResults + " of " + searchCount + " for search term: " + searchQuery +

newLine + "----


" +

newLine + resultElement[0].getTitle().replaceAll("<.*?>", “”) +

newLine + resultElement[0].getURL() +

newLine + resultElement[0].getSnippet().replaceAll("<.*?>", “”) +

newLine + "----


" +

newLine + resultElement[1].getTitle().replaceAll("<.*?>", “”) +

newLine + resultElement[1].getURL() +

newLine + resultElement[1].getSnippet().replaceAll("<.*?>", “”) +

newLine + "----


" +

newLine + resultElement[2].getTitle().replaceAll("<.*?>", “”) +

newLine + resultElement[2].getURL() +

newLine + resultElement[2].getSnippet().replaceAll("<.*?>", “”) +

newLine + "----


" +

newLine + resultElement[3].getTitle().replaceAll("<.*?>", “”) +

newLine + resultElement[3].getURL() +

newLine + resultElement[3].getSnippet().replaceAll("<.*?>", “”) +

newLine + "----


" +

newLine + resultElement[4].getTitle().replaceAll("<.*?>", “”) +

newLine + resultElement[4].getURL() +

newLine + resultElement[4].getSnippet().replaceAll("<.*?>", “”) +

newLine + "----


");

try

{

componentManager.sendPacket(this, reply);

}

catch (Exception e)

{

componentManager.getLog().error(e);

}

}

catch (GoogleSearchFault gsf)

{

gsf.printStackTrace();

Message reply = new Message();

reply.setType(message.getType());

reply.setThread(message.getThread());

reply.setTo(message.getFrom());

reply.setFrom(message.getTo());

reply.setBody(“Error searching Google, please try again”);

try

{

componentManager.sendPacket(this, reply);

}

catch (Exception e)

{

componentManager.getLog().error(e);

}

}

}

}

}[/code]

The plugin works fine, however there is a problem with unloading it after a search query has been performed. I’'m not quite sure what is causing it, any help would be great.

Some features that need to be added include, setting the apiKey as a system property in the admin console, add google spell check along with the search. And maybe add some presence code so that the component appears “online” in roster lists.

Let me know what you think.

Caleb,

Very cool idea, btw! Are you going to create a search protocol so that searches can be performed and displayed programatically?

-Matt

Since google search will only work with a developer’‘s API and that is limited to 500 queries a day…wouldn’'t that pose a problem on high volume sites?

Maybe what could be done is you could turn it into a gateway and folks could register and use their own personal account. That way, only you could overrun your limit.

A protocol would be great, especially if a client could take the results and display them in another section and not the chat window (chat window is okay if the queries are heavily formatted to not create a bunch of wrapped links).

Noah

Yes, the limitation would be a problem with sites that query alot, but like you suggested, an option for each user to input their key would solve the problem, and a seperate protocol would be cool too.

I’'m not quite sure how to go about that though…

However, back to my one of my questions, what do you guys think is preventing the plugin from unloading?

Is it the http session that the google api creates? If so, how could I close it before the plugin is unloaded?

Thanks.

What do you mean by uploading[/i] it?

Noah

Unloading?

Oops, my apologizes.

Since there is no explicit delete, the jvm will need to schedule it for delete. One way to see when it does get deleted, is to put a log message in the finalizer. That should give you an idea of how long it’‘ll take to release. Once the PluginManager releases it’‘s reference to the object, it should be free to be released. You’'ll have to sit back and wait.

What problem is the non timely unload causing?

Noah