IBM®
跳转到主要内容
    中国 [选择]    使用条款
 
 
Select a scope:Search for:    
    首页    产品    服务与解决方案     支持与下载    个性化服务    
跳转到主要内容

developerWorks 中国  >  Lotus  >

使用 IBM Lotus Sametime Java 工具包创建股票报价机器人

developerWorks
文档选项

未显示需要 JavaScript 的文档选项

讨论

样例代码


级别: 中级

Mark Talbot (talbotm@us.ibm.com), 开发人员, Industry Solutions, IBM 
Kulvir Bhogal (kbhogal@us.ibm.com), 顾问, Software Services for WebSphere, IBM

2007 年 10 月 18 日

将展示如何使用 IBM Lotus Sametime Java 工具包来创建检索 IBM Lotus Sametime Connect 股票信息的机器人。StockBot 使用 Web 服务来检索 Yahoo! Finance Web 站点的股票报价信息。

developerWorks Lotus 文章 “创建自己的 Sametime Bots, 第 1 部分” 阐述了如何创建 IBM Lotus Sametime 机器人,使用 Magic Eightball 例子来协助讨论。随着上述文章的发表及 IBM Lotus Sametime V7.5 的问世,Lotus Sametime 工具包进行了显著的重新包装。本文将讨论那些包装更改并通过创建提供股票报价信息的机器人来重新讨论机器人方面的内容。该股票报价信息是通过 IBM Rational Application Developer V6.0 所创建的 Web 服务获取的。

尽管我们重述了一些有关机器人的信息,但并未给出详尽讲解。我们建议您阅读 参考资料 中的文章来熟悉机器人这个主题。同样,如果您不熟悉 Web 服务,也可以重温 developerWorks New to SOA and Web Services 页面 上的资料。

Sametime 机器人和 IBM Lotus Sametime Java 工具包

术语机器人(bot)源自 robot 这个词,表示从用户获取指令并提供响应的软件应用程序。Sametime 机器人是一种利用了 Lotus Sametime 的到场提醒和即时消息传递功能的机器人。

对于 IBM Lotus Sametime Connect 来说,Sametime 机器人是作为 Contacts 列表中的在线联系人出现的。图 1 展示了 Lotus Sametime Connect 与 StockBot 的交互预览,它是本文所构建的机器人。


图 1. StockBot 预览
StockBot 预览

可以训练机器人,使之执行诸如向用户提供随需应变信息之类的操作,但是如何训练机器人呢?可以使用 IBM Lotus Sametime Java 工具包以编程方式实现。

Lotus Sametime Java 工具包用于桌面和服务器开发。它允许您向 Java 应用程序添加 Lotus Sametime 特性。随 IBM Lotus Sametime Software Development Kit (SDK) 提供的工具包指南将 Lotus Sametime Java Toolkit 描述为:“开发人员用于构建应用程序(这些应用程序使用了由 Lotus Sametime 提供的功能和服务)的构件块和组件的集合”。

从 Lotus Sametime V7.5 开始,将所有 Lotus Sametime 客户机和服务器工具包都捆绑到名为 Lotus Sametime SDK 的统一包中。可以从 developerworks Lotus Toolkits 页面 下载 SDK。

使用 IBM Rational Application Developer 进行开发

可以在支持 Java Development Kit 1.4.2 或 1.5 的任何 Java 开发环境中使用 Lotus Sametime 工具包。对于我们的机器人开发来说,将使用 IBM Rational Application Developer V6。Rational Application Developer 是一个基于(可加快开发速度的)Eclipse 平台的功能强大的集成开发环境(integrated development environment,IDE)。

因为本文并不是关于 Rational Application Developer 的教程,所以假定您熟悉如何使用该开发环境。如果不熟悉的话,我们建议您查看 developerWorks Rational Application Developer for WebSphere Software 页面。Rational Application Developer 随附了内置的 IBM WebSphere Application Server V6 测试环境,在开发期间可以使用它来增强企业应用程序。





回页首


使用 IBM Lotus Sametime 7.5 Demonstration Site 作为沙箱

为创建机器人,必须为它保留一个 Sametime ID。对于我们的测试和开发,将使用 IBM Lotus Sametime 7.5 Demonstration Site,其中必须创建两个 Sametime ID,一个用于机器人,而另一个用于可以访问该机器人的用户。选择 File - Preferences,然后在 Preferences 对话框的左侧导航窗格中选择 Communities,再单击 Add New Community 按钮,来配置 Lotus Sametime Connect,以便连接到 stdemo3.dfw.ibm.com 主服务器。

在随后的 Log In 附签中,输入 Sametime 用户的登录信息(Sametime Demonstration Site 用户名和密码)。单击 Server 附签,如图 2 所示,在 Host server 字段中输入 stdemo3.dfw.ibm.com,在 Community port 字段中输入 1533,然后单击 OK。


图 2. 添加 Sametime 社区用于测试和开发
添加 Sametime 社区用于测试和开发




回页首


构建并了解 StockBot

如前所述,我们正在构建用于提供股票报价信息的机器人,所以将其命名为 StockBot。问题是如何获得相关的股票报价信息?若要实现上述操作,访问由 Yahoo! Finance Web 站点提供的股票报价信息。特别地,使用 Yahoo 工具,以逗号分隔值(comma-separated value,CSV)格式返回股票报价。

下面是 IBM 股票代码请求的 CSV 返回值:

"IBM",93.86,"N/A"

特别地,我们对第二个记号(本例中的 93.86)感兴趣,它是最终成交价(通常被称为股票报价)。若要获取此类报价,必须访问与所查找的股票代码相连接的 URL:

http://finance.yahoo.com/d/quotes.csv?f=sl1e1&e=.csv&s=

例如,如果正在查找 IBM 的股票报价,URL 为

http://finance.yahoo.com/d/quotes.csv?f=sl1e1&e=.csv&s=IBM

在 URL 中使用 f=sl1e1 来指定希望 Yahoo 返回股票代码(由 s 表示)、最终成交价 (l1) 以及读数误差 (e1) (如果使用无效代码来提供服务时)。稍后,还将使用 java.net 包所提供的 Java 的内置网络工具来联系 Yahoo! Finance Web 站点 并获得所需的股票报价信息。

创建 StockQuoter 类

这是 com.devworks.example.StockQuoter 类的代码。可以从 下载 部分下载该代码以及其他与本文相关联的代码。


清单 1. com.devworks.example.StockQuoter 类
public class StockQuoter {
	private static String YAHOO_URL = 
		"http://finance.yahoo.com/d/quotes.csv?f=sl1e1&e=.csv&s=";
	Pattern p = Pattern.compile("N/A");
	private static final float 
		INVALID_SYMBOL_RETURN_VALUE = -999.999F;
	
	public float getQuote(String symbol) throws Exception
	{
		String compositeURL = YAHOO_URL + symbol;
		URL url = new URL(compositeURL);
		InputStream is = url.openStream();
		Reader reader = 
			new BufferedReader(new InputStreamReader(is));
		StreamTokenizer st = new StreamTokenizer(reader);
		// grab the first token - should be the symbol
		st.nextToken();
		String returnedSymbol = st.sval;
		if (!returnedSymbol.equals(symbol)) 
			throw 
			new Exception("A problem occurred with the stock service");
		else  
		{
			st.nextToken(); // skip the comma
			st.nextToken(); // get the quote
			float quote = (float)st.nval;
			st.nextToken(); // skip comma
			st.nextToken(); // get error
			String sError = (String)st.sval;
			Matcher myMatcher = p.matcher(sError);
			boolean found = myMatcher.find();
			if (found) return quote;
			else return INVALID_SYMBOL_RETURN_VALUE;
		}

可以看到,在该类中 java.io.InputStream 对象用于打开所请求股票代码的指定 finance.yahoo.com URL。如前所述,URL 是 finance.yahoo.com URL 和所需报价的代码的串联。使用 StreamTokenizer 类来解析通过访问 URL 而返回的输出。接着,查找股票报价所对应的记号并将它作为 float 值返回。

如果用户碰巧将无效股票代码发送到 getQuote 方法,那么 Yahoo 股票服务将返回字符串表示发生错误。如果一切都正常,则返回的字符串是 N/A。请注意正则表达式用于分析出现 N/A 时 Yahoo 的输出。如果没有发现一个输出,则将 dummy 值 -999.99F 返回给方法使用方,将 dummy 值解释为对无效股票代码的调用。

除了 getQuote 方法,该类包含主方法,可用于对 getQuote 方法进行单元测试。

上述代码必须位于在 Rational Application Developer 中创建的 Dynamic Web Project 的 Java Source 文件夹。对于我们所创建的 Dynamic Web Project,使用了名称 StockQuoteWebProject。反过来,StockQuoteWebProject 必须位于 Enterprise Application Project 中。我们将 Enterprise Project 命名为 StockQuoteEARProject。该项目结构显示在 Rational Application Developer 的 Project Explorer 中,如图 3 所示。


图 3. Project Explorer 中的 Project 结构
Project Explorer 中的 Project 结构

创建 StockQuote Web 服务

从现在这个角度来说,可以使 Sametime 机器人直接与 StockQuoter 类交互。但是,为了展示 Rational Application Developer 的 Web 服务创建工具,并使用 IDE 向导来方便地创建客户机代理,我们将展示如何创建使用 Web 服务的机器人。

若要在 Rational Application Developer 中将 com.devworks.example.StockQuoter 类公开为 Web 服务,选择 File - New - Other。在随后的对话框中,确保选中了 Show All Wizards 选项,然后展开 Web Services 目录,选择 Web Service 并单击 Next(参见图 4)。


图 4. 选择 Web Service 向导
选择 Web Service 向导

在随后的 Web Service 窗格中,确保选中了 Web 服务类型 “Java bean Web Service”,并取消选中 Generate a proxy 选项(稍后将生成代理),然后单击 Next。

在 Object Selection Page 窗格中,确保 Bean 字段中列出了 com.devworks.example.StockQuoter,然后单击 Next。在 Service Deployment Configuration 窗格中,确保将 Service project 定义为 StockQuoteWebProject,并将 EAR project 定义为 StockQuoteEARProject(参见图 5)。


图 5. 指定用于 Web 服务的项目
指定用于 Web 服务的项目

接下来,确认在 Service Endpoint Interface Selection 窗格中,bean 为 com.devworks.example.StockQuoter, service endpoint interface 为 com.devworks.example.StockQuoter_SEI。不应选中 “Use an existing service endpoint interface” 选项。然后单击 Next。

在 Web Service Java Bean Identity 窗格中,仅选择 getQuote 方法,因为不希望通过 Web 服务来公开主方法,然后单击 Next。最后在 Web Service Publication 窗格中,确保未选中 “Launch the Web Services Explorer to publish this Web service to the Unit Test UDDI Registry” 和 “Launch the Web Services Explorer to publish this Web service to a UDDI Registry” 选项,因为我们并不希望发布到 UDDI 注册库中。

不久,Rational Application Developer 将创建 Web 服务。可以使用 Rational Application Developer 中的各种工具来对 Web 服务进行单元测试。这里没有讨论该测试过程;如果不熟悉如何实现上述操作,可以参考 参考资料 部分。

请注意 Rational Application Developer 向导创建了大量代码产品,包括名为 StockQuoter.wsdl 的 Web 服务描述语言(Web Services Description Language,WSDL)文件。选择 Dynamic Web Project 并依次展开 StockQuoteWebProject、WebContent、wsdl、com、devworks 以及 example 文件夹后可以看到该文件。

创建 StockQuote Web Service Client 代理

既然有 Web 服务,则准备消费该服务。通过创建代理 Java 类来处理繁重的工作,Rational Application Developer 允许您避开如何消费 Web 服务这个复杂问题。该代理类与客户机共存,对于您来说是机器人。简单起见,假定客户机和服务器位于同一机器上。

在 Rational Application Developer 中,选择 File - New - Other(首先确保在 Select a wizard 窗口中选择了 Show All Wizards 选项)。展开 Web Services,选择 Web Service Client,然后单击 Next。在随后的 Web Services 窗格中,选择 Java proxy 作为 Client 代理类型,然后单击 Next。

接下来,需要告诉 Rational Application Developer 在创建 Web 服务时 WSDL 文件的创建位置。单击 Browse 按钮并从出现的 Resource 浏览器窗口依次展开 StockQuoteWebProject、WebContent、wsdl、com、devworks 以及 example 文件夹,再选择 StockQuoter.wsdl(参见图 6)。然后单击 OK。


图 6. 选择 StockQuoter.wsdl 文件
选择 StockQuoter.wsdl 文件

在 Client Environment Configuration 窗格中,选择 Java 作为 Client 类型,然后指定 StockBotProject 作为 Client 项目名称。稍后将看到,还将使用该项目来放置机器人。在最后一个 Web Service Client 向导窗格中,单击 Finish 来完成 Java 代理的创建(参见图 7)。


图 7. 完成 Web Service Client 向导
完成 Web Service Client 向导

现在,如果切换到 Java 透视图,应看到名为 StockBotProject 的新项目,其中有大量由 Rational Application Developer 自动生成的类(参见图 8)。代理类被命名为 StockQuoterProxy.java。


图 8. 由 Rational Application Developer 自动生成的代理类
由 Rational Application Developer 自动生成的代理类

分析 StockQuoterProxy 类,将发现名为 getQuote 的方法。稍后,将展示 StockBot 如何使用该方法找到股票报价 Web 服务。





回页首


创建 StockBot

为了开发 StockBot,使用 Lotus Sametime Java 工具包。使用 Lotus Sametime V7.5 Java 工具包,可以进行这样操作,即合并公司 Web 站点上的 Sametime 聊天,从而即时连接站点访问者和公司销售人员、定制 Lotus Sametime 来满足业务需求,或者创建自己的启用了 Sametime 的 Java 应用程序。

首先,必须进行一些内部管理,使 Java 项目为创建 Sametime 机器人做好准备。具体来说,需要将 Lotus Sametime Java 工具包的 STComm.jar、stcommsrvrtk.jar 和 stjavatk.jar 文件添加到 StockBotProject 的 Java Build Path。若要实现上述操作,请遵循以下步骤:

  1. 在 Rational Application Developer 中,双击 Package Explorer 视图中的 StockBotProject,然后选择 Properties。
  2. 在 Properties 对话框中,选择左侧窗格中的 Java Build Path 来编辑 Java Build Path 属性。
  3. 选择 Libraries 附签(参见图 9)。
  4. 单击 Add External JARs 按钮来添加 Sametime Java Client JAR 文件。前面所指定的这三个 JAR 文件包含在 Lotus Sametime Java Toolkit 中。

图 9. 编辑 Java Build Path 来包含所需的 JAR 文件
编辑 Java Build Path 来包含所需的 JAR 文件

StockService 类

Sametime 机器人由两个类组成,即 StockService 和 StockQuoteBot。大部分机器人逻辑都处于 StockService 类中的,该类实现了以下 Sametime 接口:


清单 2. Sametime 接口
com.lotus.sametime.community.LoginListener
com.lotus.sametime.community.ServiceListener
com.lotus.sametime.im.ImServiceListener
com.lotus.sametime.community.ImListener

在 StockBotProject Java 项目下的 com.devworks.example.bot 包中创建名为 StockService 的类。可以从本文的下载部分导入该类的代码。

现在来分析一下 StockService 类的内容。构造器使用表示会话名的字符串来创建新的 STSession 对象,该字符串对于运行机器人的 Java Virtual Machine 来说是惟一的。STSession 对象保存与 Sametime 会话相关联的组件集。创建了 STSession 对象后,载入机器人所使用的组件并启动 Sametime 会话。


清单 3. StockService 类
private STSession session;

public StockService(String sessionName) throws DuplicateObjectException {
		session= new STSession(sessionName);            
		session.loadSemanticComponents();
		session.start();
}

创建了 Sametime 会话后,可以加入 Sametime 社区。CommunityService 类允许 StockService 类登录 Lotus Sametime 服务器。为了使 bot 接收对请求的响应(例如登录请求),必须将 StockService 类与适当的侦听器相关联。与 StockService 类相关联的第一个侦听器是 com.lotus.sametime.community.ServiceListener。ServiceListener 提供了服务器端组件可用性的更新,来响应 CommunityService 对象上所作的请求。添加了 ServiceListener 后,将调用 private login 方法,如清单 4 所示。


清单 4. CommunityService 类
private CommunityService service;  

  public void start(String server, String username, String password)
  {
      service = (CommunityService)session.getCompApi(CommunityService.COMP_NAME);
      service.addServiceListener(this);
      login(server, username, password);
  }

在下面的 login 方法中,将 com.lotus.sametime.community.LoginListener 关联到 CommunityService 对象。因为 StockService 类也实现了 LoginListener 接口,可以用此作为参数。关联了 LoginListener 后,可以使用服务器名、用户名和密码登录 Lotus Sametime 服务器:


清单 5. login 方法
private void login(String server, String username, String password)
  {
    service.addLoginListener(this); 
    service.loginByPassword(server,username,password);
  }

com.lotus.sametime.community.LoginListener 接口有两个必须实现的回调方法。成功登录需要 loggedIn 方法,而登出需要 loggedOut 方法。在不成功登录后和成功登出后将调用 loggedOut 方法。在实现 loggedOut 方法时,查看登出的原因,并将它显示到系统控制台:


清单 6. loggedOut 方法
public void loggedOut(LoginEvent event)
  {
    int reason = event.getReason();
    if (reason == 0)
    	System.out.println("Successfully logged out.");
    else 
    	System.out.println("Failed to login.  Return Code =" + reason);
    session.stop();
    session.unloadSession();
  }
  

在下面的 loggedIn 方法中,创建了 InstantMessagingService 对象。InstantMessagingService 对象的操作类似于 CommunityService 对象,因为它有很多方法和侦听器。IMServiceListener 接口允许您侦听机器人从用户接收的消息:


清单 7. loggedIn 方法
InstantMessagingService imService;

  public void loggedIn(LoginEvent event)
  {
	imService = (InstantMessagingService) 
	_session.getCompApi(InstantMessagingService.COMP_NAME);
	imService.registerImType(ImTypes.IM_TYPE_CHAT);
	imService.addImServiceListener(this);
    System.out.println("Logged In");
  }

IMServiceListener 接口有一个回调方法 imReceived,每当用户打开新的即时消息传递窗口时,将调用该方法来启动与机器人的联系。若要侦听用户从该即时消息传递窗口发送的消息和数据,必须创建 ImListener 实现。另外,因为我们的机器人是用户友好的,所以每当用户启动与它的联系时将发送一条欢迎消息:


清单 8. imReceived 方法
String welcomeMessage = "Welcome to the StockBot. 
Enter a stock symbol.";

public void imReceived(ImEvent arg0) {
	System.out.println("im received");
	arg0.getIm().sendText(false, welcomeMessage);
	arg0.getIm().addImListener(this);
}

StockImListener 回调方法包括 dataReceived、textReceived、imClosed、openImFailed 和 imOpened。我们的机器人将仅响应 textReceived 方法和 imClosed 方法。当用户关闭其即时消息传递窗口后,将移除侦听器,防止机器人将系统资源耗费在不再使用机器人的用户上:


清单 9. imClosed 方法
public void imClosed(ImEvent arg0) {
	System.out.println("Im Closed");
	arg0.getIm().removeImListener(this);
}

StockBot 对 textReceived 回调的响应是 StockBot 获得的别名是什么。在 textReceived 的实现中,我们获得了所请求的股票代码。然后,调用 getQuoteString 来调用先前所创建的 Web 服务,getQuoteString 是一种联系股票报价 Web 服务的便利方法。如果用户键入 help,机器人将发送消息,包含用于与机器人进行交互的指令:


清单 10. textReceived 方法
String helpMessage = "Enter a stock symbol and I will return a price.";

public void textReceived(ImEvent arg0) {
	String stockSymbol = arg0.getText();
	String stringPriceMessage;
	if (stockSymbol.equals("help"))
		stringPriceMessage = helpMessage;
	else
		stringPriceMessage = getQuoteString(stockSymbol);
	arg0.getIm().sendText(true, stringPriceMessage);
}

下面的 getQuoteString 方法包含用于查找股票代码的逻辑,使用了先前所生成的 StockQuoterProxy 类。getQuoteString 方法将返回股票价格或(声明存在用户请求问题的)消息:


清单 11. getQuoteString 方法
private String getQuoteString(String stockSymbol) {
	String stringPriceMessage;
	try {
		float stockPrice = 0.0f;
		StockQuoterProxy proxy = new StockQuoterProxy();
		proxy.getQuote(stockSymbol);
  stockPrice = stockLookup.getQuote(stockSymbol.trim());
		if (stockPrice != -999.99f)
	  stringPriceMessage = 
	  String.valueOf(stockPrice);
		else
		  stringPriceMessage = 
  "I'm sorry I had trouble with your request.";	
	} catch (RemoteException e) {
		stringPriceMessage = 
"I'm sorry I had trouble with your request.";
	} catch (Exception e) {
		stringPriceMessage = 
"I'm sorry I had trouble with your request.";
	}
	return stringPriceMessage;
}

推进带有 StockQuoteBot 类的 bot

StockQuoteBot 类为 bot 提供命令行功能。该类将检索它必须连接的 Lotus Sametime 服务器信息,以及作为命令行参数的机器人的用户证书。该类使用先前所讨论的 StockService 类的实例来登录。当按下键盘上的键时,将终止 StockBot:


清单 12. StockQuoteBot 类
public class StockQuoteBot {
	public static void main(String[] args) {
		StockService service = new StockService();
		String server = args[0];
		String userID = args[1];
		String password = args[2];
		service.start(server, userID, password);
		System.out.println("Stock Quote Bot is Running.  Press any key to terminate.");
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
	try {
			br.readLine();
	} catch (IOException e) {
			System.out.println("error");
	}
		service.stop();
		System.out.println("Stock Quote Bot has terminated");
	}
}





回页首


使用 StockBot

既然完成了机器人开发,就可以在 Rational Application Developer 测试环境中方便地运行机器人了。若要实现上述操作,右击 StockQuoteBot.java 文件,然后选择 Run - Run。

现在,将显示 Run 对话框,如图 10 所示,因此有机会将命令行参数传递到 StockBot。在 Configurations 窗格中,选择 Java Application,然后单击 New 按钮。将出现用于 StockQuoteBot 类的新的 Java Application 配置。出现了该配置后,选择 Arguments 附签。对于我们的程序来说,输入的参数是 192.168.19.128 "Stock Quote Bot" ibm,它分别是 Lotus Sametime 服务器地址、用户 ID 以及密码的值。


图 10. 配置 StockBot,用于 Rational Application Developer 内的工作
配置 StockBot,用于 Rational Application Developer 内的工作

运行了 bot 类之后,将在控制台中接收确认信息,表示 Sametime 机器人成功登录(参见图 11)。


图 11. 显示 StockBot 的输出的 Rational Application Developer 控制台视图
显示 StockBot 的输出的 Rational Application Developer 控制台视图

既然 bot 正在运行,那么可以登录带有 Lotus Sametime Connect 的 Lotus Sametime 服务器,并与 StockBot 进行通信,如图 12 所示。若要实现上述操作,必须添加为 StockBot 保留的 Sametime partner ID。


图 12. 与 StockBot 交互
与 StockBot 交互




回页首


结束语

本文演示了如何创建有助于侦听并响应请求的 Sametime 机器人。特别地,我们构建了股票报价 Web 服务,它的实现将访问 Yahoo! Finance Web 站点 以获得报价信息。然后创建了机器人来访问 Web 服务;反过来,客户机可以与机器人交谈,并获得随需应变的股票报价。

在相关的 developerWorks Lotus 文章 “构建用于语言翻译的 Lotus Sametime bot” 中,展示了如何创建与 IBM WebSphere Translation Server 联合工作以提供机器翻译工作的 Sametime 机器人。可以创建的机器人类型仅受您的想象力的限制。我们希望这篇文章使您在构建自己的机器人时可以更自如地使用所需的 API。快乐地构建机器人吧!






回页首


下载

描述名字大小下载方法
StockBot 示例代码stockquoteBotExample.zip10 KBHTTP
关于下载方法的信息


参考资料

学习

获得产品和技术

讨论


作者简介

Mark Talbot 在 IBM 的 Industry Solutions 工作。您可以通过 reachtalbotm@us.ibm.com 与 Mark 联系。


Kulvir Singh Bhogal 是 IBM Software Services for WebSphere 的顾问,在全美国的客户站点上设计和实现以 J2EE 为中心的解决方案。您可以通过 kbhogal@us.ibm.com 与 Kulvir 联系。




对本文的评价

太差! (1)
需提高 (2)
一般;尚可 (3)
好文章 (4)
真棒!(5)

将您的建议发给我们或者通过参加讨论与其他人分享您的想法.




回页首


IBM 公司保留在 developerWorks 网站上发表的内容的著作权。未经IBM公司或原始作者的书面明确许可,请勿转载。如果您希望转载,请通过 提交转载请求表单 联系我们的编辑团队。
    关于 IBM 隐私条约 联系 IBM 使用条款