一个电子商务 Web 应用程序每天晚上都要处理当天收到的所有订单,并通过电子邮件向客户告知订单状态的变化。
自动执行此流程会为此 Web 应用程序的开发人员和管理员节省大量的时间,这时 IBM Workload Scheduler 就变得非常有用。
事实上,它允许您自动调度各种操作,这大大缩短了应用程序的开发时间并简化了它的管理工作。
要在 Bluemix 上实现此场景,我们可充分利用该平台提供的各种服务,并且您可轻松地将这些服务集成到应用程序中。
为了在每晚调度订单流程,我们当然能使用 Workload Scheduler 服务,我们将使用此服务在应用程序中定期调用 REST API,该 API 实现的业务逻辑可处理各种订单。
此外,我们需要一个数据库来存储订单、电子邮件地址和其他客户信息,为此,我们决定使用一个 no-SQL 数据库,然后使用 Cloudant 服务。
我们还需要一个服务来发送电子邮件,我们决定使用 SendGrid 服务。
在另一篇文章中,我介绍了一个使用 IWS Node.js 库来实现此用例的解决方案。
所以,如果您对此应用程序的 Node.js 版本感兴趣,或者希望了解完整的场景,可通过此链接找到该文章:
使用 Bluemix 上的 IBM Workload Scheduler Node.js 客户端库处理客户订单
在本文中,您刚才已看到我将使用 Liberty 运行时实现相同的用例,而且将使用 IWS Java 客户端库。
下面简要介绍一下完整的代码。
单击此处将此应用程序的 Java 版本部署到您的 Bluemix 帐户:
在本例中,WebUI 是位于 WebContent 文件夹中的 index.jsp 文件,这与 Node.js 代码版本几乎相同,但此页面上的表单会自动将请求提交到同一个 index.jsp 文件。
首先,创建一个 com.ibm.twa.bluemix.samples.ProcessOrdersIWS 对象并将其添加到会话中,如果该对象已存在,则加载它:
<%@page import="com.ibm.twa.bluemix.samples.ProcessOrdersIWS"%> <%! //Session key to register in the session private static final String WORKLOAD_APP_SESSION_KEY = "WORKLOAD_APP"; %> <% ProcessOrdersIWS poApp; if (session.getAttribute(WORKLOAD_APP_SESSION_KEY)!=null){ poApp = (ProcessOrdersIWS) session.getAttribute(WORKLOAD_APP_SESSION_KEY); }else{ poApp = new ProcessOrdersIWS(); session.setAttribute(WORKLOAD_APP_SESSION_KEY,poApp); } %>
这个类是此应用程序的主要类,充当着控制器,所以它在各种管理器(我们将会介绍)之间分配职责……
private CloudantManager cManager = new CloudantManager(); private WorkloadSchedulerManager wsManager = new WorkloadSchedulerManager();
按以下顺序使用这些方法:
1.appConnect:初始化与 WorkloadScheduler 和 Cloudant 服务的连接;
public void appConnect() throws JSONException { Manager.initConnection(); wsManager.initConnection(); }
2.isConnected:检查连接方法是否成功执行;
public boolean isConnected() { return this.wsManager.isConnected() && this.cManager.isConnected(); }
3.appCheckOrCreateProcess:检查是否存在一个 WS 流程,如果不存在,则创建它;
public void appCheckOrCreateProcess() throws InvalidRuleException, WorkloadServiceException, Exception { this.wsManager.appCheckOrCreateProcess(); }
4.existProcess:检查该流程是否存在;
public boolean existProcess() { return this.wsManager.isProcessExist(); }
5.postSubmission:创建一个提交并将其保存到 Cloudant 数据库中;
public void postSubmission(String address, String subject, String body) throws Exception { this.cManager.postSubmission(address, subject,body); }
6.isSubmitted:检查提交是否已成功保存;
public boolean isSubmitted(){ return this.cManager.isSubmitted(); }
所有这些方法都被路由到一个或多个扩展 com.ibm.twa.bluemix.samples.managers.Manager 类的类。
所以 Manager 是一个抽象类,它包含:
initConnection():此方法向 VCAP_SERVICES 环境变量检索一个通用服务的凭据并使用它进行连接。要想正常运行,此方法需要一个能够从 VCAP_SERVICES 获取每个服务的正确凭据的 switch:
switch(this.serviceName){ case "cloudantNoSQLDB": this.user = (String) credentials.get("username"); this.host = (String) credentials.get("host"); this.password = (String) credentials.get("password"); this.url = (String) credentials.get("url"); this.port = (int) credentials.get("port"); break; case "WorkloadScheduler": this.user = (String) credentials.get("userId"); this.password = (String) credentials.get("password"); this.url = (String) credentials.get("url"); break; case "sendgrid": this.user = (String) credentials.get("username"); this.password = (String) credentials.get("password"); this.host = (String) credentials.get("hostname"); break; }
它还需要通过构造函数方法获取具体的服务名称:
public Manager(String serviceName){ this.serviceName = serviceName; }
connect():这是一个由特定服务所实现的抽象方法(所有类都扩展了 Manager.java),并通过以下方式创建与这些服务的连接:
WorkloadSchedulerManager: public void connect() { try { this.ws = new WorkloadService(this.getUrl()); } catch (MalformedURLException e) { e.printStackTrace(); } catch (WorkloadServiceException e) { e.printStackTrace(); } } CloudantManager: public void connect(){ this.cloudantClient = new CloudantClient(this.getUrl(), this.getUser(), this.getPassword()); } SendGridManager: public void connect(){ this.sendgrid = new SendGrid(this.getUser(), this.getPassword()); }
现在我们可将精力放在包 com.ibm.twa.bluemix.samples.managers 中已扩展的管理器的最重要具体方法上:
在 WorkloadSchedulerManager 中,我们可找到 appCheckOrCreateProcess() 方法,它负责:
1. 按名称获取一个流程库,或者如果该库不存在,则创建一个
TaskLibrary lib = this.getProcessLibraryByName(WorkloadSchedulerManager.processLibraryName); if(lib == null){ System.out.println("Library not found"); System.out.println("Creating library..."); lib = new TaskLibrary(); lib.setName(WorkloadSchedulerManager.processLibraryName); lib.setParentId(-1); lib = ws.createTaskLibrary(lib); } else{ System.out.println(lib.getName() + " process library found"); }
2. 按名称以及刚获取的流程库获取一个流程……
Task task = this.getProcessByName(lib, WorkloadSchedulerManager.processName);
3. 如果该流程不存在,则通过以下方式创建它:
// Create the Workload Automation Process WAProcess process = new WAProcess("processOrdersIWS", "Process orders using IWS"); // Retrieve the tenantId value (from the url property) int index = super.getUrl().indexOf("tenantId=") + 9; String prefix = super.getUrl().substring(index, index + 2); this.agentName = prefix + "_CLOUD"; // Set the Restful URL String url = "http://www.yourapp.mybluemix.net/api/sendemail/"; // Create the RestfulStep object and add it to the Workload Automation Process RestfulStep restStep = new RestfulStep(agentName, url, "application/json", "application/json", RestfulStep.GET_METHOD); process.addStep(restStep); process.setTaskLibraryId(lib.getId()); // Create a trigger that allows to the Workload Automation process // to run every night at the 23:00 (11PM) Trigger trigger = TriggerFactory.everyDayAt(23, 00); process.addTrigger(trigger); // Create and Enable the Workload Automation Process // After being instantiated, a process has to be created and activated // on the server before it can be triggered to run according // to the specified schedule. try { System.out.println("Creating and enabling the process"); Task createdTask = ws.createAndEnableTask(process); this.myProcessId = createdTask.getId(); this.processExist = true; } catch (Exception e) { System.out.println(e.getClass().getName() + " " + e.getMessage()); }
在 CloudantManager 中,我们可找到:
1.postSubmission(String address, String subject, String body),它创建一个提交并将其保存在数据库上
public Response postSubmission(String address, String subject, String body) throws Exception { if(this.cloudantClient == null){ this.connect(); } this.subId = UUID.randomUUID().toString(); Submission submission = new Submission(this.subId, address, subject, body); Database db = this.getDbByName(CloudantManager.cloudantDatabaseName); Response response = db.post(submission); this.submitted = true; return response; }
2.getAllSubmissions(),它检索并返回数据库上已保存的所有提交
public List getAllSubmissions() throws JSONException{ this.db = this.getDbByName(CloudantManager.cloudantDatabaseName); if(db != null){ this.submissions = db.view("_all_docs").includeDocs(true).query(Submission.class); } return this.submissions; }
3. 在 SendGridManager 中,我们可找到 send() 方法
public String send() throws JSONException{ if(!this.isConnected()){ this.initConnection(); } if(!this.cManager.isConnected()){ this.cManager.initConnection(); } this.submissions = this.cManager.getAllSubmissions(); String resp = "Email sent to:</br>"; SendGrid.Response response = null; for(Submission sub : this.submissions){ if(!sub.getStatus().equalsIgnoreCase("completed")){ this.sgemail = new SendGrid.Email(); try { System.out.println("Sending email to:" + sub.getAddress()); this.sgemail.addTo(sub.getAddress()); this.sgemail.setFrom("gabdibonaventura@gmail.com"); this.sgemail.setSubject(sub.getSubject()); this.sgemail.setText(sub.getBody()); response = this.sendgrid.send(this.sgemail); } catch (SendGridException e) { e.printStackTrace(); sub.setStatus("failed"); this.cManager.getDb().update(sub); } if(response.getStatus()){ resp += sub.getAddress() + "</br>"; sub.setStatus("completed"); this.cManager.getDb().update(sub); } else{ resp += sub.getAddress() + " not sent because:" + response.getMessage(); sub.setStatus("failed"); this.cManager.getDb().update(sub); } } this.sgemail = null; } return resp; }
Submission 类帮助我们管理 Cloudant 文档,它包含电子邮件细节(地址、主题和正文),提交日期和提交状态(“submitted”、“completed”或“failed”)等信息。
package com.ibm.twa.bluemix.samples.helpers; import java.util.Calendar; public class Submission { private String _id; private String _rev; private String address; private String subject; private String body; private String status; private Calendar startDate; public Submission(String _id, String address, String subject, String body){ this._id = _id; this.address = address; this.subject = subject; this.body = body; this.status = "submitted"; this.setStartDate(Calendar.getInstance()); } //getters and setters }
Workload Scheduler 步骤中 REST 风格的调用被提交到 com.ibm.twa.bluemix.samples.jaxrs.SendEmail 类,具体来讲提交到 sendEmail() 方法。
public Response sendEmail() throws SendGridException, JSONException { this.sgManager = new SendGridManager(); this.sgManager.initConnection(); String response = this.sgManager.send(); return Response.ok().entity(response).build();
现在您已准备好将此应用程序部署在 Bluemix 环境上,并轻松、快速地完成您的工作。一个在几年前需要经验丰富的程序员参与并且需要大量时间的工作,现在只需几次单击即可完成,然后您只需单击代码概述内容顶部的 Deploy 按钮。