论道WP(三):应用程序栏
作者通过具体翔实的例子介绍了Winodws Phone 7中应用程序栏的使用方式。
该内容已经被标记书签!
标记书签错误,请重试!

作者 Boris Lublinsky, Mike Segel 译者 侯伯薇 发布于 2011年10月20日
在前面的两篇文章中[1,2],我们描述了Oozie工作流服务器,并且展示了几个工作流的示例。我们还描述了针对Oozie的工作流的部署和配置,以及用来启动、停止和监控Oozie工作流的工具。
在本文中,我们会向你展示Oozie的可扩展性,并说明它是如何支持我们实现自定义的、协同工作的语言扩展。
正如我们在文章[1]中所说明的,Oozie之所以与众不同,是因为它提供了一种“最小化”的工作流语言,其中只包含少数几种控制和动作节点。尽管其中的一种动作节点是java动作节点,它让我们可以从Oozie工作流调用任意一个带有main方法的java类,但这种方法并非总是最佳的。原因之一就在于,java动作是在Hadoop簇集中作为map-reduce作业执行的,并且只带有唯一的Mapper任务。一方面,这带来了很多好处:
另一方面,这种方法也有一些缺点:
尽管如此,在运行时间相对较长(几分钟甚至几小时)的map/reduce或者Pig作业中,好处会大大超过负载的缺点,但是,在简单的java节点中(参见[2]),我们就需要注意外部执行所导致的开销了。所以,使用自定义动作节点的原因之一,就是为了支持在Oozie的执行上下文中直接执行轻量级的java类[1]。
使用自定义动作的另一个原因是为了提高工作流的语义和可读性。由于Oozie是一种支持基于Hadoop处理组件的工作流引擎,所以它的语法完全是以Hadoop执行为中心的——Hadoop文件系统、map/reduce、Pig等等。这种语法能够很好地符合Hadoop开发者的习惯,但是并没有涉及到太多关于给定动作的功能信息。我们可以为动作本身制定与业务相关的命名转换规则,但这只是特别用来解决问题的——对动作的命名只反映了给定过程的语法,既不是总体上的主题领域,也不能解决动作参数的问题,那些问题仍然只能由开发者来解决。
幸运的是,Oozie支持非常棒的扩展机制——自定义动作节点[3],它让我们可以很容易地解决这两个问题。自定义的动作节点让我们可以使用附加的动作(动词)来扩展Oozie的语言。Oozie的动作节点可以是同步的,也可以是异步的。
在这个例子中,我们会对独立的邮件程序进行转换,并展现到自定义的email动作中[2](代码1)。
package com.navteq.assetmgmt.oozie.custom;
import java.util.Properties;
import java.util.StringTokenizer;
import javax.mail.Message;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import org.apache.oozie.ErrorCode;
import org.apache.oozie.action.ActionExecutor;
import org.apache.oozie.action.ActionExecutorException;
import org.apache.oozie.action.ActionExecutorException.ErrorType;
import org.apache.oozie.client.WorkflowAction;
import org.apache.oozie.util.XmlUtils;
import org.jdom.Element;
import org.jdom.Namespace;
public class EmailActionExecutor extends ActionExecutor {
private static final String NODENAME = "eMail";
private static final String SUCCEEDED = "OK";
private static final String FAILED = "FAIL";
private static final String KILLED = "KILLED";
private static final String DEFAULMAILSERVER = "imailchi.navtech.com";
private static final String EMAILSERVER = "emailServer";
private static final String SUBJECT = "emailSubject";
private static final String MESSAGE = "emailBody";
private static final String FROM = "emailFrom";
private static final String TO = "emailTo";
public EmailActionExecutor() {
super(NODENAME);
}
@Override
public void check(Context context, WorkflowAction action) throws ActionExecutorException {
// Should not be called for synch operation
throw new UnsupportedOperationException();
}
@Override
public void end(Context context, WorkflowAction action)throws ActionExecutorException {
String externalStatus = action.getExternalStatus();
WorkflowAction.Status status = externalStatus.equals(SUCCEEDED) ?
WorkflowAction.Status.OK : WorkflowAction.Status.ERROR;
context.setEndData(status, getActionSignal(status));
}
@Override
public boolean isCompleted(String arg0) {
return true;
}
@Override
public void kill(Context context, WorkflowAction action) throws ActionExecutorException {
context.setExternalStatus(KILLED);
context.setExecutionData(KILLED, null);
}
@Override
public void start(Context context, WorkflowAction action) throws ActionExecutorException {
// Get parameters from Node configuration
try{
Element actionXml = XmlUtils.parseXml(action.getConf());
Namespace ns = Namespace.getNamespace("uri:custom:email-action:0.1");
String server = actionXml.getChildTextTrim(EMAILSERVER, ns);
String subject = actionXml.getChildTextTrim(SUBJECT, ns);
String message = actionXml.getChildTextTrim(MESSAGE, ns);
String from = actionXml.getChildTextTrim(FROM, ns);
String to = actionXml.getChildTextTrim(TO, ns);
// Check if all parameters are there
if(server == null)
server = DEFAULMAILSERVER;
if((message == null) || (from == null) || (to == null))
throw new ActionExecutorException(ErrorType.FAILED,
ErrorCode.E0000.toString(), "Not all parameters are defined");
// Execute action synchronously
SendMail(server, subject, message, from, to);
context.setExecutionData(SUCCEEDED, null);
}
catch(Exception e){
context.setExecutionData(FAILED, null);
throw new ActionExecutorException(ErrorType.FAILED,
ErrorCode.E0000.toString(), e.getMessage());
}
}
// Sending an email
public void SendMail(String server, String subject, String message,
String from, String to) throws Exception {
// create some properties and get the default Session
Properties props = new Properties();
props.setProperty("mail.smtp.host", server);
Session session = Session.getDefaultInstance(props, null);
// create a message
Message msg = new MimeMessage(session);
// set the from and to address
InternetAddress addressFrom = new InternetAddress(from);
msg.setFrom(addressFrom);
// To is a comma separated list
StringTokenizer st = new StringTokenizer(to, ",");
String [] recipients = new String[st.countTokens()];
int rc = 0;
while(st.hasMoreTokens())
recipients[rc++] = st.nextToken();
InternetAddress[] addressTo = new InternetAddress[recipients.length];
for (int i = 0; i < recipients.length; i++){
addressTo[i] = new InternetAddress(recipients[i]);
}
msg.setRecipients(Message.RecipientType.TO, addressTo);
// Setting the Subject and Content Type
msg.setSubject(subject);
msg.setContent(message, "text/plain");
Transport.send(msg);
}
}
代码1: Email自定义动作
这个实现对ActionExecutor[2]类(由Oozie提供)进行了扩展,并重写了一些必要的方法。因为邮件的发送过程是一种非常快速的操作,所以我们决定将其实现为同步的动作处理程序,那意味着它会在Oozie的执行上下文中执行。
我们的实现(代码1)遵循了Oozie文档并实现了所有必需的方法:
实现了自定义的动作执行方式之后,我们需要为新的email动作定义XML模式[5](代码2)。
<?xml version="1.0" encoding="UTF-8"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:email="uri:custom:email-action:0.1" elementFormDefault="qualified" targetNamespace="uri:custom:email-action:0.1"> <xs:complexType name="EMAIL"> <xs:sequence> <xs:element name="emailServer" type="xs:string" minOccurs="0" maxOccurs="1" /> <xs:element name="emailSubject" type="xs:string" /> <xs:element name="emailFrom" type="xs:string" /> <xs:element name="emailTo" type="xs:string" /> <xs:element name="emailBody" type="xs:string" /> </xs:sequence> </xs:complexType><xs:element name="eMail" type="email:EMAIL"></xs:element>
</xs:schema>
代码2: 为email组件所用的XML schema
自定义动作节点和XML schema文件都需要打包在单独的jar文件中,比方说emailAction.Jar。我们可以使用Oozie的oozie-setup.sh脚本执行下面的命令,从而把这个(以及其他所有)jar文件添加到Oozie的war文件中。
$ bin/oozie-setup.sh -jars emailAction.jar:mail.jar (See Adding Jars to Oozie)
代码3: 部署命令
|
向Oozie添加Jar文件 你要知道,Cloudera推荐的oozie-setup.sh命令行会重新构建你的war文件,并且, 如果你使用网页来监控作业,那么就会丢失java的脚本扩展。在测试方面,我们难以同时包含-extjs和-jars选项。作为权宜之计,我们会把jar文件复制到${CATALINA_BASE}/webapps/oozie/WEB-INF/lib中,其中${CATALINA_BASE}代表的是/var/lib/oozie/oozie-server。请注意,这里存在一定的风险,如果其他人重新构建了war文件,那么你就会丢失这些扩展,并且他们以后需要手动添加。对于测试来说,我们建议复制jar文件,然而,对于生产环境,我们建议把jar添加到war文件中。 |
现在我们需要把关于自定义执行器的信息注册到Oozie的运行时中。这是通过扩展oozie-site.xml完成的[6]。我们可以通过在Oozie配置文件oozie-site.xml中添加或者修改“oozie.service.ActionService.executor.ext.classes”[7]来注册自定义动作本身(代码4)。
…………………………………… <property> <name>oozie.service.ActionService.executor.ext.classes</name> <value>com.navteq.assetmgmt.oozie.custom. EmailActionExecutor </value> </property> ……………………………………
代码4: 自定义执行配置
为新动作(代码2)所用的XML schema应该添加到oozie-site.xml中,位于属性“oozie.service.WorkflowSchemaService.ext.schemas”[8]之下(代码5)。
……………………………………… <property> <name>oozie.service.SchemaService.wf.ext.schemas</name> <value> emailAction.xsd</value> </property> …………………………………
代码5: 自定义模式配置
最后,Tomcat启动之后,我们就可以在工作流中使用自定义的动作节点了。
为了测试我们的实现,我们创建了简单的工作流(代码6),它会使用我们的执行器来发送email。
<!--
Copyright (c) 2011 NAVTEQ! Inc. All rights reserved.
Test email Oozie Script
-->
<workflow-app xmlns='uri:oozie:workflow:0.1' name='emailTester'>
<start to='simpleEmail'/>
<action name='simpleEmail'>
<eMail xlmns=“ uri:custom:email-action:0.1”>
<emailSubject>test</emailSubject>
<emailFrom>mike.segel@<mycompany>.com</emailFrom>
<emailTo>boris.lublinsky@<mycompany>.com</emailTo>
<emailMessage>This is a test message, if you can see this, Mikey did something right! :)</emailMessage>
</eMail>
<error to="fail"/>
<ok to="end"/>
</action>
<kill name="fail">
<message>Workflow failed, error message[${wf:errorMessage(wf:lastErrorNode())}]</message>
</kill>
<end name='end'/>
</workflow-app>
代码6: 使用email自定义动作执行器的简单工作流
在这篇文章中,我们展示了如何通过创建自定义的动作执行器来扩展Oozie。这样做让我们可以定义和实现部门或者企业专用的Oozie语言(领域专用语言),其中具有部门或者企业的功能。这样的领域专用语言能够简化特定部门或者企业的构建过程,并提高代码的可读性。
尽管Oozie相对还不够成熟,但是它已经为处理包含多个map/reduce作业、并且能够向总体的业务过程添加非map-reduce作业的过程提供了基本的框架。随着越来越多的用户使用Oozie并提供反馈,我们相信它有足够的潜力,可以成为Hadoop环境中强大的集成部分。
对于Oozie来说,还有很多我们没有在这三篇文章中包含的内容。我们只是期望它们可以引起你对Oozie工作流引擎的足够兴趣,并且能够成为进一步研究Oozie的不错的起点。
Boris Lublinsky是NAVTEQ公司的首席架构师,在这家公司中他的工作是为大型数据管理和处理、SOA以及实现各种NAVTEQ的项目定义架构的愿景。他还是InfoQ的SOA编辑,以及OASIS的SOA RA工作组的参与者。Boris是一位作者,还经常发表演讲,他最新的一本书是《Applied SOA》。
Michael Segel在过去二十多年间一直与客户写作,识别并解决他们的业务问题。Michael已经作为多种角色、在多个行业中工作过。他是一位独立顾问,总是期望能够解决所有有挑战的问题。Michael拥有俄亥俄州立大学的软件工程学位。
[1] 这种类的例子可能是各种计数器操作、简单的计算等等。
[2] 所有Oozie动作执行器都是Oozie分发程序的一部分,它们都是通过扩展这个类实现的。
[3] 在我们的实现中,我们使用的是默认的实现,这样就是为什么没有在代码中展现的原因。你可以查看一下Oozie的源代码[4],就可以知道在现存的Oozie动作处理程序中是如何实现这个方法的。
[4] 配置自定义执行器有两种方式——工作流变量和/或动作配置。在我们的例子中展示的是后者,但是在实际情况中,实际上总是二者的组合。
[5] 确保不仅要定义复杂的类型,还要对元素进行定义。那是Oozie所期望的。
[6] 通常在Oozie的分发包中叫做oozie-default.xml。
[7] 对于多个执行器,类名应该以逗号分隔。
[8] 对于多次执行,我们可以为多种模式使用逗号分隔的列表。
查看英文原文:Extending Oozie
给InfoQ中文站投稿或者参与内容翻译工作,请邮件至editors@cn.infoq.com。也欢迎大家加入到InfoQ中文站用户讨论组中与我们的编辑和其他读者朋友交流。
在多线程并发编程中Synchronized一直是元老级角色,很多人都会称呼它为重量级锁,但是随着Java SE1.6对Synchronized进行了各种优化之后,有些情况下它并不那么重了,本文详细介绍了Java SE1.6中对于锁的性能优化,以及锁的存储结构及升级过程。
本次分享将首先介绍现代富文本编辑器的组成和实现,然后结合UEditor的开发过程,与参会者分享UEditor在设计和实现的过程中,所涉及到的核心功能的细节实现。
本次演讲视频录制于百度技术沙龙。
我们所开发的应用程序大多都需要提供一个图形用户界面(GUI)。关于GUI应用的架构设计,已经有了Form & Control、MVC,、MVP、 Passive View等多种模式。模式可以帮助我们建立优雅的架构,但前提是弄清楚模式的应用场景。弄清楚GUI应用面临的设计上的问题,有助于我们正确的挑选设计方案。
MongoDB是一种非常易用的NoSQL方案,Brian C. Dilley在这篇文章里介绍了MongoDB的优劣势,并介绍了MJORM项目。MJORM用于MongoDB,是一个没有注解的Java ORM库。
随着网络基础设施的逐步成熟,从RPC进化到Web Service,并在业界开始普遍推行SOA,再到后来的RESTful平台以及云计算中的PaaS与SaaS概念的推广,分布式架构在企业应用中开始呈现出不同的风貌,然而殊途同归,这些分布式架构的目标仍然是希望回到建造巴别塔的时代,系统之间的交流不再为不同语言与平台的隔阂而产生障碍。
精益软件开发方法因其对市场和交付的重视和在各种场景下体现出的适应能力正在获得广泛的关注。特别是在精益创业(Lean Startup)渐渐兴起和技术日新月异的今天,其"极端"的思想也变得越来越必要和可行。 InfoQ就此主题对他做了深入的采访。
没有回复
关注此讨论 回复