Ooze 扩展自定义action节点

大家好,我是crazy_老中医,我写程序就像老中医一样,全凭感觉和经验,但是有用!

废话不多说,现在开始正文,本文将阐述如何扩展一个自定义oozie action节点。

准备阶段

oozie安装

略。

个人建议用Ambari管理工具进行安装,省心省事。

扩展action节点功能分析

本文扩展的action节点主要实现:

1、根据URL参数发送http请求(应用场景:工作流执行完毕后向通知业务平台做一些事情);

2、根据状态参数(status)控制节点运行完毕状态(应用场景:控制工作流正常结束或者异常结束);

编码实现

项目结构

 

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>cn.com.cloudpioneer</groupId>
    <artifactId>DDPAction</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.apache.oozie</groupId>
            <artifactId>oozie-core</artifactId>
            <version>4.2.0</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>commons-httpclient</groupId>
            <artifactId>commons-httpclient</artifactId>
            <version>3.1</version>
            <exclusions>
                <exclusion>
                    <artifactId>commons-codec</artifactId>
                    <groupId>commons-codec</groupId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.jdom</groupId>
            <artifactId>jdom</artifactId>
            <version>1.1</version>
        </dependency>
    </dependencies>

    <build>
        <finalName>DDPAction</finalName>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>*.*</include>
                </includes>
                <filtering>true</filtering>
            </resource>
        </resources>
        <plugins>
            <plugin>
                <artifactId>maven-assembly-plugin</artifactId>
                <configuration>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                </configuration>
                <executions>
                    <execution>
                        <id>make-assembly</id>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

 

RequestUrlAction.java

package cn.com.cloudpioneer.oozie.action;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.oozie.action.ActionExecutor;
import org.apache.oozie.action.ActionExecutorException;
import org.apache.oozie.client.WorkflowAction;
import org.apache.oozie.service.ConfigurationService;
import org.apache.oozie.service.SchemaService;
import org.apache.oozie.util.IOUtils;
import org.apache.oozie.util.XLog;
import org.apache.oozie.util.XmlUtils;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.Namespace;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.*;

/**
 * Created by yangtongjie on 2017/6/9.
 */
public class RequestUrlAction extends ActionExecutor {

    private static XLog LOG = XLog.getLog(RequestUrlAction.class);

    private static String TYPE = "request-url";
    private static String URL = "url";
    private static String ARG = "arg";
    private static String STATUS = "status";

    public RequestUrlAction(){
        super(TYPE);
    }

    protected RequestUrlAction(String type) {
        super(TYPE);
    }

    @Override
    public void initActionType() {
        super.initActionType();

        try {
            String file = org.apache.commons.codec.binary.Base64.class.getProtectionDomain().getCodeSource().getLocation().getFile();
            System.err.println("initActionType:" + file);
            String s = new Base64(0).encodeToString("asdfasdfasdf".getBytes());
            System.err.println("initActionType:" + s);
        }catch (Exception ex){
            ex.printStackTrace();
        }
    }

    @Override
    public void start(Context context, WorkflowAction action) throws ActionExecutorException {
        try {
            Element actionXml = XmlUtils.parseXml(action.getConf());
            Namespace ns = actionXml.getNamespace();

            String url = actionXml.getChildTextTrim(URL, ns);
            String status = actionXml.getChildTextTrim(STATUS, ns);
            String parameters = "";
            List children = actionXml.getChildren(ARG, ns);
            for(Object obj : children){
                Element child = (Element) obj;
                String param = child.getText();
                parameters += "&" + param;
            }

            String request = url + "?status=" + status;
            request = parameters.equals("") ? request : request + parameters;

            doGet(request);

            if("ERROR".equals(status))
                throw new ActionExecutorException(ActionExecutorException.ErrorType.ERROR, "DDPAction:" + this.getClass(), "Normal Error.");
        } catch (JDOMException e) {
            e.printStackTrace();
        } catch (Exception ex){
            ex.printStackTrace();
            if(ex instanceof ActionExecutorException) throw (ActionExecutorException)ex;
            else throw new ActionExecutorException(ActionExecutorException.ErrorType.ERROR, "parameters error", ex.getMessage());
        }

        context.setExecutionData("OK", null);
    }

    @Override
    public void end(Context context, WorkflowAction action) throws ActionExecutorException {
        String externalStatus = action.getExternalStatus();
        WorkflowAction.Status status = externalStatus.equals("OK") ? WorkflowAction.Status.OK :
                WorkflowAction.Status.ERROR;
        context.setEndData(status, getActionSignal(status));
    }

    @Override
    public void check(Context context, WorkflowAction action) throws ActionExecutorException {

    }

    @Override
    public void kill(Context context, WorkflowAction action) throws ActionExecutorException {

    }

    @Override
    public boolean isCompleted(String externalStatus) {
        return true;
    }

    public static void doGet(String url){
        HttpClient client = new HttpClient();
        GetMethod method = new GetMethod(url);
        try {
            client.executeMethod(method);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //释放连接
            method.releaseConnection();
        }
    }

    private static void scanInnerSchemas() throws IOException {
        String[] schemas = ConfigurationService.getStrings(SchemaService.WF_CONF_SCHEMAS);
        Set<String> schemaNames = new HashSet<String>();
        if (schemas != null) {
            for (String schema : schemas) {
                schema = schema.trim();
                if (!schema.isEmpty()) {
                    schemaNames.add(schema);
                }
            }
        }

        for (String schemaName : schemaNames) {
            System.err.println("schema:" + schemaName);
            InputStream is = IOUtils.getResourceAsStream(schemaName, -1);
            if(is != null){
                try {
                    BufferedReader br = new BufferedReader(new InputStreamReader(is));
                    String line = br.readLine();
                    while (line != null) {
                        System.err.println(line);
                        line = br.readLine();
                    }
                }catch (Exception ex){
                    ex.printStackTrace();
                }
            }else {
                System.err.println("schema:input stream is null.");
            }
        }
    }

    public static void main(String[] args) throws JDOMException {
//        String conf = "        <request-url xmlns=\"uri:custom:ddp-action:0.1\">\n" +
//                "            <url>http://developback.ddp.dacube.com.cn/task/warning/email</url>\n" +
//                "\t\t\t<status>ERROR</status>\n" +
//                "\t\t\t<arg>subId=90dde13a-3c49-4922-82fd-ad54645bab43</arg>\n" +
//                "\t\t\t<arg>taskId=3926b022-5986-41ec-9ee7-cc22b082c4ca</arg>\n" +
//                "        </request-url>";
//        Element actionXml = XmlUtils.parseXml(conf);
//        Namespace ns = actionXml.getNamespace();
//
//        String url = actionXml.getChildTextTrim(URL, ns);
//        String status = actionXml.getChildTextTrim(STATUS, ns);
//        List<String> params = new ArrayList<String>();
//        String parameters = "";
//        List children = actionXml.getChildren(ARG, ns);
//        for(Object obj : children){
//            Element child = (Element) obj;
//            String param = child.getText();
//            params.add(param);
//            parameters += "&" + param;
//        }
//
//        String request = url + "?status=" + status;
//        request = params.size() == 0 ? request : request + parameters;
//
//        System.err.println(request);

        doGet("http://localhost:9090/task/warning/email?status=FINISHED&taskId=3926b022-5986-41ec-9ee7-cc22b082c4ca&subId=e130ff20-137b-4b86-a594-2a8a6bbbf2a3");

        System.err.println();
    }
}

 

request-url.xsd

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
           xmlns:request-url="uri:custom:ddp-action:0.1"
           elementFormDefault="qualified"
           targetNamespace="uri:custom:ddp-action:0.1">
    <xs:complexType name="ACTION">
        <xs:sequence>

            <xs:element name="url" type="xs:string" minOccurs="1" maxOccurs="1"/>
            <xs:element name="status" type="xs:string" minOccurs="1" maxOccurs="1"/>
            <xs:element name="arg" type="xs:string" minOccurs="1" maxOccurs="unbounded"/>

        </xs:sequence>
    </xs:complexType>
    <xs:element name="request-url" type="request-url:ACTION"></xs:element>
</xs:schema>

 

打包:略

 

部署到oozie

找到oozie安装目录,并将扩展的action jar包拷贝到libext目录下,如下图:

 

添加配置到oozie-site.xml

    <property>
      <name>oozie.service.ActionService.executor.ext.classes</name>
      <value>cn.com.cloudpioneer.oozie.action.RequestUrlAction</value>
    </property>
    <property>
      <name>oozie.service.SchemaService.wf.ext.schemas</name>
      <value>request-url.xsd</value>
    </property>

 

重启oozie,如果不出意外,你的扩展节点已经可用了。

 

测试

workflow.xml

<workflow-app xmlns="uri:oozie:workflow:0.5" name="V2SubTest">
    <start to="t"/>
    <kill name="t-kill">
        <message>Action failed, error message[${wf:errorMessage(wf:lastErrorNode())}]</message>
    </kill>
    <action name="t">
        <request-url xmlns="uri:custom:ddp-action:0.1">
            <url>http://developback.ddp.dacube.com.cn/task/warning/email</url>
	    <status>ERROR</status>
	    <arg>subId=90dde13a-3c49-4922-82fd-ad54645bab43</arg>
	    <arg>taskId=3926b022-5986-41ec-9ee7-cc22b082c4ca</arg>
        </request-url>
        <ok to="t-End"/>
        <error to="t-kill"/>
    </action>
    <end name="t-End"/>
</workflow-app>
	    <arg>taskId=3926b022-5986-41ec-9ee7-cc22b082c4ca</arg>
        </request-url>
        <ok to="t-End"/>
        <error to="t-kill"/>
    </action>
    <end name="t-End"/>
</workflow-app>

 

job.properties

oozie.use.system.libpath=true
oozie.wf.application.path=hdfs://master:8020/user/ddp/t
security_enabled=False
dryrun=False

 

运行结果

因为在workflow.xml中我配置了

<status>ERROR</status>

所以最终的运行结果是:ERROR。不理解的同学请读写RequestUrlAction.java就会知道,这里不赘述。

心得

如果你对oozie比较熟悉的话,你会发现,扩展oozie整个过程很简单,但是对于不熟悉的同学,肯定没有那么轻松。比如我就历时4个工作日才将oozie扩展成功,在扩展的过程中首先查阅的相关资料,资料写得很简明扼要(事实也是如此),但是对于oozie架构(这里指编码实现)不大了解的同学来说,本文中提到的“部署”阶段就会比较陌生,甚至有时不知所措,我就是这样,不知道是我搜索能力不行还是查阅文档的能力不行,我在网上没有找到我所遇到问题的答案,只能看oozie源码,其实大部分时间都是花在看源码上,通过源码了解到扩展节点是怎么被引用,加载,以及运行的整个过程,了解了这些,那扩展一个oozie节点就相当的得心应手了,祝你成功。

 

参考资料

http://oozie.apache.org/docs/4.2.0/DG_CustomActionExecutor.html

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值