Sparkplug开发人员指南
Sparkplugs是什么?
Sparkplugs动态扩展Spark即时通讯客户端。使用Sparkplugs定制Spark,为您的企业或组织开发即时通讯系统,我们希望通过插件的API的描述,让你在开发灵活的Spark客户端变得更加简单,并充满使用乐趣。
本指南提供了SparkAPI和几种常见的例子,如果想更深入的了解请直接转到Javadocs文档中。
我编译了它(Spark),然后呢?
当你编译了超酷的Sparkplug后,就可以很容易的发布给用户,只要将你开发的插件拖到Spark的插件安装路径下,如果你的Sparkplug是普遍有用的,我们希望您能将你的插件分享到Spark社区,将你的Sparkplug插件发送到plugins@jivesoftware.org中,建立jivesoftware.org公共信息库
内容
本文件包含以下信息:
² Spark客户端概述
² Sparkplug API概述
² Sparkplug的Java文档
² 插件结构
² 准备开始写你的第一个插件
² Spark例子和使用方法。在开发Spark的例子和常见的问题。
Spark客户端概述
Spark客户端的设计理念是让不同层次的用户熟悉和使用它,无论是Workspace还是ChatRoom,都可以根据您的需要访问它。
Spark API概述
Spark API提供了一个用于添加之上的协议和/或客户端UI Spark扩展框架。例如,你可以写自己的消息过滤器或添加一个按钮,一个聊天室和使用文件传输的API实现传送文件。Spark API具有以下特征:
² 通过事件*********拦截,得到通知,并响应特定事件IM的自定义代码。
允许定制用户界面,从简单的图标更改,添加按钮,以及添加自己的菜单项。
² 能够使用SMACK API添加自己的XMPP协议的功能。
² 管理器(Managers):Spark客户端为开发插件提供延迟加载的接口。相关的管理器的有:
n SparkManager—SparkManager的核心管理模块器。你可以使用这个管理器来获得ChatManager,SessionManager,SoundManager和UserManager实例。
n ChatManager—负责处理最登记聊天听众和过滤器,以及聊天室的创建和检索。
它也可以用来检索ChatFrame用户界面。
n SessionManager --包含有关当前会话信息,如连接到服务器,连接的错误和个人存在变动的通知处理。
n SoundManager --用于播放声音。
² 事件处理 (Event Handlers):Spark包含了大量的监听和处理程序,以便把更多插件可以嵌入到Spark client。比较常见的监听和处理程序有:
n ChatRoomListener(和ChatRoomListenerAdapter)- 允许的插件监听聊天室被打开,关闭和启动。通常你会使用这个自定义的个人聊天室。
n MessageListener --消息接收或发送*********
n ContactGroupListener --联络组变动*********。
n ContactListListener --联系人列表变动*********。
n FileTransferListener --拦截文件传输*********。
n ContextMenuListener --右键弹出菜单(上下文菜单)*********,允许添加、删除动作或添加删除菜单项。
n PresenceListener –人员状态变化*********。
n ContactItemHandler – ContactItem在线状态变化控制监听。
² 组件(Components):Spark包含了需要在你开发插件时使用的Swing组件,较为常用的有:
n MainWindow --该框架包含联系人列表。您可以使用MainWindow添加新的标签,菜单项或焦点控件。
n ChatRoom –聊天室基础的抽象类。它的实现是ChatRoomImpl和GroupChatRoom。
n ChatArea –为TranscriptWindow和ChatInputEditor的基本聊天视图控件。
n ContactList –Spark的联系人列表界面控件
n ChatRoomButton –聊天室的外观按钮,用于适应聊天室的主题。
Plugin的结构
Plugins是JAR的压缩文件,插件的文件结构如下:
Plugin Structure(压缩包机构) |
myplugin.jar!/ |- plugin.xml <- Plugin 定义文件
|- libs/ <- 运行插件(Plugin)所用到的类. |
plugin.xml文件中指定的主要Plugin类。样例文件如下所示:
plugin.xml样例 |
<?xml version="1.0" encoding="UTF-8"?> <!-- Google Plugin Structure -->
<plugin> <name>Google Desktop Plugin</name> <class>com.examples.plugins.GooglePlugin</class> <author>Derek DeMoro</author> <version>1.0</version> <description>Enables users to find files and emails relating to users using Google Desktop technology.</description> <email>ddman@jivesoftware.com</email> <minSparkVersion>2.0.6</minSparkVersion> </plugin> |
Plugin的安装
你只需要将要安装的插件JAR拖放到你的Spark客户端安装插件目录中。
目录机构 |
Spark/ |- plugins/ <- 将你的SparkPlug插件放到这里。
|- lib/ <-运行你的插件需要的类和类库。 |- resources/ <- 资源文件夹,包括其他支持文档和类库. |- docs/ <-你自己的开发插件的帮助手册和的JavaDoc. |
你的插件类必须实现从Spark客户端API插件接口。该插件接口是用于初始化和关闭插件的方法。
开始编写Sparkplugs
为了建立自己的SparkPlugs,你需要将工程属性中的所有项目设置到ClassPath中,将Sparkplug.zip 或Sparplug.tar.gz解压缩,你将看到以下内容:
Sparkplugs/ |- build/ <-使用ANT让你轻松的构建你的插件。
|- images/ <- 文档使用的图像文件夹。 |- plugin_development_guide.html/ <-完整的开发指南(本文档)。 |- api/ <- Sparkplug包含的Javadoc文档。 |- spark/ <- spark构建结构,你需要设置Classpath。 |-bin <- 启动文件startup.bat,用于测试Spark。 |-lib <- 包含所有Spark运行需要的包。 |-logs <- 所有的日志存储。 |-plugins <- 所有的插件(plugins)部署到这里。 |-resources <- 包含用于运行在具体操作系统上的本机库。 |
要设置一个Spark项目,运行在你的IDE,您将需要以下几件事:
² 编译Spark,需要1.4以上版本的JRE;
² 将Sparkplugs/spark/lib和Sparkplugs/spark/lib/windows文件夹添加到Classpath;
² 添加资源目录(Sparkplugins/sparkresource)到本地Classpath中;
² Main Class - org.jivesoftware.Spark;
² VM参数--Dplugin=path_to_your_plugin.xml文件,这使您的插件可以运行在IDE以外的环境中;
² 就这些。
我们提供了可以轻松的构建、部署你的插件到你的的Spark测试环境中。
在使用时
Building a SparkplugTo easily build a Sparkplug, we have added a simple ANT script to create a deployed plug. To create, do the following:
1) Copy your java source files to the src directory. 2) Place any dependencies (besides Spark) into the lib directory. 3) Update the plugin.xml file to represent your plugin. 4) Go to the build directory, and type ant jar to build your plugin or .... type "ant run" to build and deploy your plugin directly to Spark and have Spark startup to test your plugin right away. Your new plugin will be called myplugin.jar. If you wish to deploy your plugin later, just copy your new myplugin.jar to the plugins directory of your Sparkplug distro kit. Enjoy! |
Spark是如何做的呢?
所有代码示例可以在examples.jar文件找到。
² 如何创建一个简单的插件
² 如何将我自己的标签到Spark工作空间
² 如何添加一个上下文菜单监听到联系人列表
² 如何添加ContextMenu监听到一个聊天室
² 如何添加菜单到Spark
² 如何添加一个按钮到聊天室
² 如何添加自己喜欢的Spark搜索,比如用户搜索或谷歌搜索在Firefox
² 怎么能拦截文件传输请求
² 如何发送一个文件给其他用户
² 如何控制用户界面和事件的ContactItem处理
² Spark发生变化时,怎么能通知用户更改时
² 如何添加消息筛选器
² 如何创建一个人对人的聊天室
² 如何创建一个公共会议室
² 如何添加自己的首选项
² 当有新消息是如果进行消息提示
如何创建一个简单的插件
- 实现Plugin
Plugin样例 |
package org.jivesoftware.spark.examples;import org.jivesoftware.spark.plugin.Plugin;
/** * Implements the Spark Plugin framework to display the different possibilities using * Spark. */ public class ExamplePlugin implements Plugin { /** * Called after Spark is loaded to initialize the new plugin. */ public void initialize() { System.out.println("Welcome To Spark"); } /** * Called when Spark is shutting down to allow for persistence of information * or releasing of resources. */ public void shutdown() { } /** * Return true if the Spark can shutdown on users request. * @return true if Spark can shutdown on users request. */ public boolean canShutDown() { return true; } /** * Is called when a user explicitly asked to uninstall this plugin. * The plugin owner is responsible to clean up any resources and * remove any components install in Spark. */ public void uninstall(){ // Remove all resources belonging to this plugin. } } |
如何将我自己的标签到Spark工作空间
- 实现Plugin;
- 取得Spark用户界面;
- 取得WorkspacePane,这是Spark的Tab面板;
- 添加您自己的Tab
添加Tab到 Spark |
public class ExamplePlugin implements Plugin {.. /**
* Adds a tab to Spark */ private void addTabToSpark(){ // Get Workspace UI from SparkManager Workspace workspace = SparkManager.getWorkspace(); // Retrieve the Tabbed Pane from the WorkspaceUI. JTabbedPane tabbedPane = workspace.getWorkspacePane(); // Add own Tab. tabbedPane.addTab("My Plugin", new JButton("Hello")); }. } |
如何添加一个上下文菜单监听到联系人列表
- 实现Plugin;
- 在Spark's Workspace中取得ContactList
- 添加ContactListListener
添加 ContextMenu监听到ContactList |
private void addContactListListener(){ // Get Workspace UI from SparkManager
Workspace workspace = SparkManager.getWorkspace(); // Retrieve the ContactList from the Workspace ContactList contactList = workspace.getContactList(); // Create an action to add to the Context Menu final Action sayHelloAction = new AbstractAction() { public void actionPerformed(ActionEvent actionEvent) { JOptionPane.showMessageDialog(SparkManager.getMainWindow(), "Welcome to Spark"); } }; sayHelloAction.putValue(Action.NAME, "Say Hello To Me"); // Add own Tab. contactList.addContextMenuListener(new ContextMenuListener() { public void poppingUp(Object object, JPopupMenu popup) { if(object instanceof ContactItem){ popup.add(sayHelloAction); } } public void poppingDown(JPopupMenu popup) { } public boolean handleDefaultAction(MouseEvent e) { return false; } }); } |
如何添加ContextMenu监听到一个聊天室
- 实现Plugin
- 添加ChatRoomListener到ChatManager
- 从ChatRoom获取TranscriptWindow或ChatInputEditor
- 添加ContactMenuListener到ChatArea
添加ChatRoomListener到ChatManager,TranscriptWindow or ChatInputEditor |
private void addContactListenerToChatRoom() { // Retrieve a ChatManager from SparkManager
ChatManager chatManager = SparkManager.getChatManager(); final ContextMenuListener listener = new ContextMenuListener() { public void poppingUp(Object object, JPopupMenu popup) { final TranscriptWindow chatWindow = (TranscriptWindow)object; Action clearAction = new AbstractAction() { public void actionPerformed(ActionEvent actionEvent) { try { chatWindow.insert("My own text "); } catch (BadLocationException e) { e.printStackTrace(); } } }; clearAction.putValue(Action.NAME, "Insert my own text"); popup.add(clearAction); } public void poppingDown(JPopupMenu popup) { } public boolean handleDefaultAction(MouseEvent e) { return false; } }; // Add a ChatRoomListener to the ChatManager to allow for notifications // when a room is being opened. Note: I will use a ChatRoomListenerAdapter for brevity. chatManager.addChatRoomListener(new ChatRoomListenerAdapter() { public void chatRoomOpened(ChatRoom room) { room.getTranscriptWindow().addContextMenuListener(listener); } public void chatRoomLeft(ChatRoom room) { room.getTranscriptWindow().removeContextMenuListener(listener); } }); } |
如何添加菜单到Spark
- 实现Plugin
从SparkManager取得MainWindow
- 创建一个菜单或是菜单项到现有的菜单中
添加菜单到Spark |
/** * Adds a new menu and child menu item to Spark.
*/ private void addMenuToSpark(){ // Retrieve the MainWindow UI from Spark. final MainWindow mainWindow = SparkManager.getMainWindow(); // Create new Menu JMenu myPluginMenu = new JMenu("My Plugin Menu"); // Create Action to test Menu install. Action showMessage = new AbstractAction() { public void actionPerformed(ActionEvent actionEvent) { JOptionPane.showMessageDialog(mainWindow, "Yeah, It works."); } }; // Give the menu item a name. showMessage.putValue(Action.NAME, "Check if it works"); // Add to Menu myPluginMenu.add(showMessage); // Add Menu To Spark mainWindow.getJMenuBar().add(myPluginMenu); } |
如何添加一个按钮到聊天室
- 实现Plugin
- 添加ChatRoomListener 到 ChatManager
- 打开房间时,添加ChatRoomButton到ChatRoom的ToolBar上
新增一个按钮到ChatRoomButton |
/** * Adds a button to each Chat Room that is opened.
*/ private void addChatRoomButton(){ // Retrieve ChatManager from the SparkManager ChatManager chatManager = SparkManager.getChatManager(); // Create a new ChatRoomButton. final ChatRoomButton button = new ChatRoomButton("Push Me"); // Add to a new ChatRoom when the ChatRoom opens. chatManager.addChatRoomListener(new ChatRoomListenerAdapter() { public void chatRoomOpened(ChatRoom room) { room.getToolBar().addChatRoomButton(button); } public void chatRoomLeft(ChatRoom room) { room.getToolBar().removeChatRoomButton(button); } }); } |
如何添加自己Spark搜索功能,如用户搜索或Firefox上的谷歌搜索
- 实现Plugin
- 创建一个可搜索对象来实现Searchable接口
- 添加Searchable实现到SearchManager
添加搜索功能到Spark,如用户搜索或Firefox上的谷歌搜索 |
/** * Called after Spark is loaded to initialize the new plugin.
*/ public void initialize() { // Register new Searchable object "SearchMe" with the SearchManager. SearchManager searchManager = SparkManager.getSearchManager(); searchManager.addSearchService(new SearchMe()); } See the SearchMe code below. package org.jivesoftware.spark.examples; import org.jivesoftware.spark.search.Searchable; import org.jivesoftware.spark.SparkManager; import org.jivesoftware.resource.LaRes; import javax.swing.Icon; import javax.swing.JOptionPane;
/** * A simple example of how to integrate ones own search into Spark. */ public class SearchMe implements Searchable { /** * The icon to show in the search box. * @return the icon. */ public Icon getIcon() { return LaRes.getImageIcon(LaRes.SMALL_AGENT_IMAGE); } /** * Returns the name of this search object that is displayed in the drop down box. * @return the name. */ public String getName() { return "Searches Nothing Really"; } /** * Returns the text that should be displayed in grey when this searchable object * is initially selected. * @return the text. */ public String getDefaultText() { return "Click to search me."; } /** * Returns the text to display in the tooltip. * @return the tooltip text. */ public String getToolTip() { return "Shows an example of integrating ones own search into Spark."; } /** * Is called when a user hits "Enter" key. * @param query the query the user is searching for. */ public void search(String query) { JOptionPane.showMessageDialog(SparkManager.getMainWindow(), "Nothing Found "); } } |
怎么能拦截文件传输请求
- 实现Plugin
- 实现TransferListener
- 注册你的TransferListener
拦截的文件传输请求 |
/** * Listen for incoming transfer requests and either handle them yourself, or pass them
* off to be handled by the next listener. If no one handles it, then Spark will handle it. */ private void addTransferListener(){ SparkTransferManager transferManager = SparkManager.getTransferManager(); transferManager.addTransferListener(new TransferListener() { public boolean handleTransfer(FileTransferRequest request) { // If I wanted to handle it, take the request, accept it and get the inputstream. // Otherwise, return false. return false; } }); } |
如何发送一个文件给其他用户
- 实现Plugin
- 通过UserManager获取用户的完整的JID
- 获取SparkTransferManager和发送文件
发送文件到其他用户 |
/** * Sends a file to a user in your ContactList.
*/ private void sendFile(){ // Retrieve SparkTransferManager from the SparkManager. SparkTransferManager transferManager = SparkManager.getTransferManager(); // In order to send a file to a person, you will need to know their full Jabber // ID. // Retrieve the Jabber ID for a user via the UserManager. This can // return null if the user is not in the ContactList or is offline. UserManager userManager = SparkManager.getUserManager(); String jid = userManager.getJIDFromNickname("Matt"); if(jid != null){ transferManager.sendFile(new File("MyFile.txt"), jid); } } |
如何控制用户界面和事件的ContactItem处理
- 实现Plugin
获取ContactList
- 通过用户的JID获取ContactItem(s)基本信息
- 添加自己ContactItemHandler到ContactItem
控制用户界面和事件处理的ContactItem |
/** * Controls the UI of a ContactItem.
*/ private void handleUIAndEventsOfContactItem(){ ContactList contactList = SparkManager.getWorkspace().getContactList(); ContactItem item = contactList.getContactItemByJID("paul@jivesoftware.com/spark"); ContactItemHandler handler = new ContactItemHandler() { /** * Called when this users presence changes. You are responsible for changing the * icon (or not) of this contact item. * @param presence the users new presence. */ public void handlePresence(Presence presence) { } /** * Is called when a user double-clicks the item. * @return true if you are handling the event. */ public boolean handleDoubleClick() { return false; } }; item.setHandler(handler); } |
Spark发生变化时,怎么能通知用户
- 实现Plugin
- 从SparkManager 中获取SessionManager
- 添加自己的PresenceListener到PresenceListener
Spark发生变化时通知用户 |
/** * Allows a plugin to be notified when the Spark users changes their
* presence. */ private void addPersonalPresenceListener(){ SessionManager sessionManager = SparkManager.getSessionManager(); sessionManager.addPresenceListener(new PresenceListener() { /** * Spark user changed their presence. * @param presence the new presence. */ public void presenceChanged(Presence presence) { } }); } |
如何添加消息筛选器
- 实现Plugin
- 从SparkManager中获取ChatManager
- 创建Message Filter实例
- 注册到ChatManager
添加自己的信息过滤器 |
/** * Installs a new MessageFilter.
*/ private void installMessageFilter() { // Retrieve the ChatManager from SparkManager ChatManager chatManager = SparkManager.getChatManager(); MessageFilter messageFilter = new MessageFilter() { public void filter(Message message) { String currentBody = message.getBody(); currentBody = currentBody.replaceAll("bad words", "good words"); message.setBody(currentBody); } }; chatManager.addMessageFilter(messageFilter); // Just remember to remove your filter if need be. } |
如何创建一个人对人的聊天室
- 实现Plugin
- 从 SparkManager 获取ChatManager
- 使用ChatManager 创建一个新的ChatRoom
- 使用ChatContainer激活创建的ChatRoom
创建一个人对人的聊天室 |
/** * Creates a person to person Chat Room and makes it the active chat.
*/ private void createPersonToPersonChatRoom(){ // Get the ChatManager from Sparkmanager ChatManager chatManager = SparkManager.getChatManager(); // Create the room. ChatRoom chatRoom = chatManager.createChatRoom("don@jivesoftware.com", "Don The Man", "The Chat Title"); // If you wish to make this the active chat room. // Get the ChatContainer (This is the container for all Chat Rooms) ChatContainer chatContainer = chatManager.getChatContainer(); // Ask the ChatContainer to make this chat the active chat. chatContainer.activateChatRoom(chatRoom); } |
如何创建一个公共会议室
- 实现Plugin
- 从 SparkManager 获取ChatManager
- 使用ChatManager创建一个会议聊天室(Conference ChatManager)
- 使用ChatContainer激活创建的ChatRoom
创建一个会议聊天室 |
/** * Creates a person to person Chat Room and makes it the active chat.
*/ private void createConferenceRoom() { // Get the ChatManager from Sparkmanager ChatManager chatManager = SparkManager.getChatManager(); Collection serviceNames = null; // Get the service name you wish to use. try { serviceNames = MultiUserChat.getServiceNames(SparkManager.getConnection()); } catch (XMPPException e) { e.printStackTrace(); } // Create the room. ChatRoom chatRoom = chatManager.createConferenceRoom("BusinessChat", (String)serviceNames.toArray()[0]); // If you wish to make this the active chat room. // Get the ChatContainer (This is the container for all Chat Rooms) ChatContainer chatContainer = chatManager.getChatContainer(); // Ask the ChatContainer to make this chat the active chat. chatContainer.activateChatRoom(chatRoom); } } |
如何添加自己的首选项
- 实现Plugin
- 创建一个类实现Preference
- 使用Preference创建一个自己的界面
- 用PreferenceManager注册新的Preference
创建Preference |
/** * $RCSfile: ,v $ * $Revision: $ * $Date: $ * * Copyright (C) 1999-2005 Jive Software. All rights reserved. * * This software is the proprietary information of Jive Software. * Use is subject to license terms. */ package org.jivesoftware.spark.examples.preferences;
import org.jivesoftware.spark.preference.Preference; import org.jivesoftware.resource.LaRes;
import javax.swing.Icon; import javax.swing.JComponent;
public class MyPreferences implements Preference { private MyPreferenceUI ui; public MyPreferences(){ ui = new MyPreferenceUI(); } public String getTitle() { return "Example Preferences"; } public Icon getIcon() { return LaRes.getImageIcon(LaRes.ADD_IMAGE_24x24); } public String getTooltip() { return "Example tooltip in preference dialog"; } public String getListName() { return "Examples"; } public String getNamespace() { return "EXAMPLE"; } public JComponent getGUI() { return ui; } public void load() { // Would load persisted information from file or server and // set the UI appropriately. ui.setShowChatHistory(true); } public void commit() { // Would persist the current state of the preferences. boolean showChatHistory = ui.isChatHistoryShown(); } public boolean isDataValid() { return true; } public String getErrorMessage() { return null; } public Object getData() { return null; } public void shutdown() { // Do nothing. } }
package org.jivesoftware.spark.examples.preferences; import org.jivesoftware.spark.util.ResourceUtils; import javax.swing.JCheckBox; import javax.swing.JPanel; import java.awt.FlowLayout; /** * Demonstrates a simple panel used to display a UI that can be used as * the Preference UI in the Preferences Dialog. This panel shows a simple * UI with accessors for setting the preference values / persistence. */ public class MyPreferenceUI extends JPanel { private JCheckBox showChatHistory; /** * Creates the default panel using FlowLayout as the Layout. But * GridBagLayout is the really only true layout */ public MyPreferenceUI() { setLayout(new FlowLayout(FlowLayout.LEFT)); buildUI(); } private void buildUI() { showChatHistory = new JCheckBox(); // Use Mnemonics for the CheckBox using ResourceUtils. ResourceUtils.resButton(showChatHistory, "&Show Chat History in Chat Window"); // Add Button add(showChatHistory); } /** * Sets the UI based on previous preferences. * @param show true if Chat History to show up. */ public void setShowChatHistory(boolean show){ showChatHistory.setSelected(show); } /** * Returns true if Chat History should be shown. * @return true if history shown. */ public boolean isChatHistoryShown(){ return showChatHistory.isSelected(); } }
public void addPreference(){ PreferenceManager preferenceManager = SparkManager.getPreferenceManager(); preferenceManager.addPreference(new MyPreferences()); } |
当接收到新消息时,如果进行消息提示
当接收到新消息时,如果进行消息提示 |
// Get the ChatContainer from the ChatManager. ChatContainer chatContainer = ChatManager.getChatContainer();
// Get the room you wish to be notified. ChatRoom chatRoom = chatContainer.getActiveChatRoom(); chatContainer.startFlashing(chatRoom); |