搭建Pentaho7.0 插件(Web方向)快速开发环境

方寒
2023-12-01

搭建Pentaho7.0 插件(Web方向)快速开发环境

Pentaho是一个以工作流为核心的、强调面向解决方案而非工具组件的BI套件,整合了多个开源项目,目标是和商业BI相抗衡。它偏向于与业务流程相结合的BI解决方案,侧重于大中型企业应用。它允许商业分析人员或开发人员创建报表,仪表盘,分析模型,商业规则和 BI 流程。

在开发Pentaho插件的过程中,最直接遇到的问题是,Pentaho启动速度太慢,我们想测试我们写的插件的时候,必须重新打jar包,并覆盖相关的配置,再重新启动Pentaho加载插件,这样一来,测试的效率就及其低下了,在对Pentaho不熟悉的情况下,想要通过Pentaho那少的及其可怜的文档,和Pentaho的Tomcat日志来定位分析解决插件中的bug,就更难了。

所以搭建一个Pentaho插件的快速开发环境就很有必要了,以下将围绕这点展开,所用的Pentaho的版本是7.0

一、简要分析Pentaho结构

想要开发Pentaho的插件,我们重点关注两个文件夹,“/pentaho-solutions/system”和“tomcat”,一个是将来插件放置的位置,另一个是Pentaho启动用到的内置Tomcat,将来开发部署到Pentaho进行测试的过程中,需要查看Tomcat的log。

二、Pentaho插件如何部署?

可先参考Pentaho官方的这篇文档,文档有点旧,但大体上做个参考没太大的问题。

Developing Plugins https://wiki.pentaho.com/display/ServerDoc2x/Developing+Plugins

Pentaho插件需要在“/pentaho-solutions/system”下新建一个目录,我们假定为“my-plugin”。

  • 新建lib文件夹,用于放置所开发的插件的jar包以及其依赖的jar包。
  • 新建“plugin.spring.xml”文件,文件内容其实就是spring的配置文件,Pentaho自带了一个spring 4.3.2,相关的jar包在“/tomcat/webapps/pentaho/WEB-INF/lib”文件夹。
  • 新建“plugin.xml”,用于配置Pentaho界面上,显示插件的链接
  • 新建“resources”文件夹,用于放置插件的html页面等资源文件。

完成以上步骤后,启动pentaho,在logs文件夹中的pentaho.log可以看到插件的加载情况。

三、Pentaho 插件快速开发环境搭建

++该快速开发环境最终使用的框架主要有 spring,jersey,mybatis++,我们需要达成的目标是

  • 可以脱离pentaho环境,独立快速运行
  • 引入orm框架,方便数据存取
  • 在开发后,可以直接编译打包工程,尽量不用更改配置项

3.1 使用idea 创建 gradle 项目,build.gradle

plugins {
    id 'java'
    id 'idea'
}

group 'com.xxx'
version '1.0-SNAPSHOT'

sourceCompatibility = 1.8

repositories {
    mavenCentral()
}

dependencies {
    compile group: 'org.hibernate.validator', name: 'hibernate-validator', version: '6.0.13.Final'
    compile group: 'com.sun.jersey', name: 'jersey-core', version: '1.19.1'
    compile group: 'com.sun.jersey', name: 'jersey-server', version: '1.19.1'
    compile group: 'com.sun.jersey', name: 'jersey-client', version: '1.19.1'
    compile(group: 'com.sun.jersey', name: 'jersey-json', version: '1.19.1')
    compile group: 'com.sun.jersey', name: 'jersey-bundle', version: '1.19.4'
    compile group: 'com.sun.jersey.contribs', name: 'jersey-multipart', version: '1.19.1'
    compile group: 'com.sun.jersey.contribs', name: 'jersey-spring', version: '1.19.1'
    compile group: 'com.sun.jersey', name: 'jersey-servlet', version: '1.19.1'
    compile group: 'org.springframework.boot', name: 'spring-boot-starter-actuator', version: '2.0.5.RELEASE'
    compile group: 'org.mybatis.spring.boot', name: 'mybatis-spring-boot-starter', version: '1.3.2'
    compile group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: '2.0.5.RELEASE'
    compile group: 'org.springframework.boot', name: 'spring-boot-devtools', version: '2.0.5.RELEASE'
    runtime "mysql:mysql-connector-java:8.0.12"
    compile group: 'com.microsoft.sqlserver', name: 'mssql-jdbc', version: '6.1.0.jre8'
    compile 'org.apache.commons:commons-dbcp2:2.5.0'
    compile 'commons-dbcp:commons-dbcp:1.4'
    compile group: 'javax.servlet', name: 'javax.servlet-api', version: '4.0.1'
    compile fileTree(dir: 'jars', include: ['*.jar'])
    testCompile group: 'org.springframework.boot', name: 'spring-boot-starter-test', version: '2.0.5.RELEASE'
    testCompile group: 'junit', name: 'junit', version: '4.12'
}

//run "gradle build copyJars"
//run "gradle copyJars"
task copyJars(type: Copy) {
    from configurations.runtime
    into './build/libs'
}

关注几个重点:

  • 注意依赖包的版本,如果你想模拟更真实的pentaho环境,不要随意调整依赖的版本号,可根据自己使用pentaho的版本,可能需要依赖包的版本也有所变化,可以在“/Users/fenixadar/Documents/CDS Doc/pentaho-server/tomcat/webapps/pentaho/WEB-INF/lib”查看自己应该选择的版本号。
  • 另外,注意加载的包里面,是否有冲突,比如,如果你想使用swagger,注意swagger的版本,以及swagger所依赖的jersey和当前的是否一致,以及是否需要排除该包,别问我是怎么知道的。如上,所使用的jersey1,应该寻找对应依赖jaersey的swagger。
  • jars文件夹中有
kettle-core-7.0.0.0-25.jar
kettle-engine-7.0.0.0-25.jar
metastore-7.0.0.0-25.jar
pentaho-database-model-7.0.0.0-25.jar
pentaho-platform-api-7.0.0.0-25.jar
pentaho-platform-build-utils-7.0.0.0-25.jar
pentaho-platform-core-7.0.0.0-25.jar
pentaho-platform-extensions-7.0.0.0-25.jar
pentaho-platform-repository-7.0.0.0-25.jar
pentaho-platform-scheduler-7.0.0.0-25.jar
pentaho-reporting-engine-classic-core-7.0.0.0-25.jar
pentaho-reporting-engine-classic-core-platform-plugin-7.0.0.0-25.jar
  • 最后的task,run “gradle copyJars” 是用于把所有的依赖包拷贝到libs文件夹下,部署的时候一起拷贝到pentaho的lib下,可参考上面如何部署的章节。

3.2 关联使用JAXB标准的Jersey

新建类 JerseyConfig

@Configuration
@Order(Ordered.HIGHEST_PRECEDENCE)
public class JerseyConfig implements ServletContextInitializer {
    public JerseyConfig() {
    }
    @Override
    public void onStartup(ServletContext servletContext) {

        final SpringServlet servlet = new SpringServlet();
        final ServletRegistration.Dynamic appServlet = servletContext.addServlet("appServlet", servlet);
        appServlet.setLoadOnStartup(1);
        final Set<String> mappingConflicts = appServlet.addMapping("/*");
        if (!mappingConflicts.isEmpty()) {
            throw new IllegalStateException("'appServlet' cannot be mapped to '/' under Tomcat versions <= 7.0 .14 ");
        }
    }
}

3.3 关联使用MyBatis

这里需考虑,在最后部署到pentaho上的时候,是需要使用pentaho提供的数据源接口的,而在开发时,我们只需要直连数据库就可以了。

在plugin.spring.xml中增加bean,如下:

    <bean id="dataSource" class="com.xxx.xxx.DbConfig" factory-method="getDataSource"/>

    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <property name="mapperLocations" value="classpath:com/xxx/mapper/**/*Mapper.xml"/>
        <property name="typeAliasesPackage" value="com.xxx.xxx.xxx"/>
    </bean>
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.xxx.xxx.mapper"/>
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
    </bean>

新建类DbConfig,假设pentaho的数据源是 pth,代码如下:

public class DbConfig {
    public static final String DEV = "DEV";
    public static final String PROD = "PROD";

    public static DataSource getDataSource() {
        String current = "DEV";
        if (current.equals(DEV)) {
            BasicDataSource dataSource = new BasicDataSource();
            dataSource.setDriverClassName("com.mysql.jdbc.Driver");
            dataSource.setUrl("jdbc:mysql://localhost:3306/zzz?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8");
            dataSource.setUsername("root");
            dataSource.setPassword("root");
            return dataSource;
        } else if (current.equals(PROD)) {
            return new LazyDataSource();
        }
        return null;
    }

    static class LazyDataSource extends AbstractDataSource {
        @Override
        public Connection getConnection() {
            try {
                return getDataSource().getConnection();
            } catch (ObjectFactoryException | DBDatasourceServiceException | SQLException e) {
                e.printStackTrace();
            }
            return null;
        }

        @Override
        public Connection getConnection(String username, String password) {
            try {
                return getDataSource().getConnection(username, password);
            } catch (ObjectFactoryException | DBDatasourceServiceException | SQLException e) {
                e.printStackTrace();
            }
            return null;
        }

        public DataSource getDataSource() throws ObjectFactoryException, DBDatasourceServiceException {
            final IDBDatasourceService datasourceService = PentahoSystem.getObjectFactory()
                    .get(IDBDatasourceService.class, PentahoSessionHolder.getSession());
            return datasourceService.getDataSource("pth");
        }
    }
}

定义的“mybatis-config.xml”,内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true" />
        <setting name="logImpl" value="LOG4J" />
    </settings>
</configuration>

3.4 api开发

api开发涉及的几个要点:

  • 如何标识为bean,以及注入service

在“plugin.spring.xml”中添加

    <bean id="exampleApi" class="com.xxx.xxx.api.ExampleApi" scope="prototype">
        <property name="exampleService" ref="exampleService"/>
    </bean>

可以看到,这和普通的spring mvc添加bean方式是一样的,因为pentaho内置了spring。
但我尝试通过注解的方式添加bean,失败了。

新建类 ExampleApi

public class ExampleApi {
    private SalesForecastingService salesForecastingService;
    @Inject
    public SalesForecastingApi(SalesForecastingService salesForecastingService) {
        this.salesForecastingService = salesForecastingService;
    }
    
    public SalesForecastingService getSalesForecastingService() {
        return salesForecastingService;
    }
    public void setSalesForecastingService(SalesForecastingService salesForecastingService) {
        this.salesForecastingService = salesForecastingService;
    }
}

@Inject是javax.inject包下的注解。

  • 如何描述http提交方式,接受与输出内容类型,标识访问路径等?

在ExampleApi添加方法后,内容如下:

@Path("/xxx/api/yyy")
public class ExampleApi {

    private ExampleService exampleService;

    @Inject
    public ExampleApi(ExampleService exampleService) {
        this.exampleService = exampleService;
    }

    @GET
    @Path("/getExample")
    @Consumes(MediaType.TEXT_PLAIN)
    @Produces(MediaType.TEXT_PLAIN)
    public String getExample(@QueryParam("name") String name) {
        return "hello " + name;
    }

    @POST
    @Path("/postExample")
    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
    @Produces(MediaType.TEXT_PLAIN)
    public String postExample(@FormParam("name") String name) {
        return "hello" + name;
    }


    @POST
    @Path("/uploadExample")
    @Consumes(MediaType.MULTIPART_FORM_DATA)
    @Produces(MediaType.TEXT_PLAIN)
    public String uploadExample(
            @QueryParam("name") String name,
            @FormDataParam("file") InputStream file) {
        return "导入成功";
    }

    public ExampleService getExampleService() {
        return exampleService;
    }

    public void setExampleService(ExampleService exampleService) {
        this.exampleService = exampleService;
    }
}

现在来分析下代码

  • @Path标识了该api资源的路径,这和我们经常使用的@RequestMapping功能一样。
  • @GET 表示其接受get请求,这和@GetMapping功能一样,同样,@POST表示其接受post请求,和@PostMapping功能一样。
  • @Consumes 和 @Produces 表示接受与输出内容类型
  • @QueryParam @FormParam 和 @FormDataParam 是接受的参数的提交方式
    其实,这些就是 JAXB的规范,也可以直接看 Jersey 1.19.1 User Guide https://jersey.github.io/documentation/1.19.1/index.html
  • 我特意附带了一个上传文件的示例,上传文件时,需标注“@Consumes(MediaType.MULTIPART_FORM_DATA)”,文件本身需用“@FormDataParam”,这是jaxb规范,但这边有一个注意点,如果你还有别的参数,不能使用@FormDataParam,否则就会出错,file 就不会被正确解析还原为上传的文件流,你还得自己解析,那就相当麻烦了,不完美的快速解决方案是通过@QueryParam,把参数写在请求路径中。

3.5 service层

service层就比较简单了,如下:
新建类ExampleService

public class ExampleService {

    ExampleMapper exampleMapper;
    
    public String hello() {
       return "hello world";
    }

    public ExampleMapper getExampleMapper() {
        return exampleMapper;
    }

    public void setExampleMapper(ExampleMapper exampleMapper) {
        this.exampleMapper = exampleMapper;
    }
}

同时注意“plugin.spring.xml”,我们之前就在api里面定义了该service了,现在也要定义service,并注入mapper。

    <bean id="exampleService" class="com.xxx.xxx.service.ExampleService" scope="prototype">
        <property name="exampleMapper" ref="exampleMapper"/>
    </bean>

3.5 mapper层与model层

mapper 与 model层和通常的并没有太大区别,只是注意下,要在“plugin.spring.xml”里面定义mapper的搜索位置,不需要在mapper类上注解@Mapper。参考 3.3 关联使用MyBatis

3.7 application 以及 plugin.spring.xml

因为我们引入了spring boot,所以在测试的环境下一样需要Application。代码很简单,如下:

@SpringBootApplication
@ImportResource(value = {"classpath:plugin.spring.xml"})
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

注意,我们没有定义application.yaml,也没有@Controller,@Service的注解,所以此处,我们一样通过@ImportResource来注解类。

至此,“plugin.spring.xml”文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">

    <!--测试环境下注释此行-->
    <bean id="api" class="org.pentaho.platform.web.servlet.JAXRSPluginServlet"/>

    <context:annotation-config/>

    <bean id="dataSource" class="com.xxx.xxx.DbConfig" factory-method="getDataSource"/>

    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <property name="mapperLocations" value="classpath:com/xxx/xxx/mapper/**/*Mapper.xml"/>
        <property name="typeAliasesPackage" value="com.xxx.xxx.xxx.entity"/>
    </bean>
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.xxx.xxx.mapper"/>
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
    </bean>

    <bean id="exampleApi" class="com.xxx.xxx.api.ExampleApi" scope="prototype">
        <property name="exampleService" ref="exampleService"/>
    </bean>
    <bean id="exampleService" class="com.xxx.xxx.service.ExampleService" scope="prototype">
        <property name="exampleMapper" ref="exampleMapper"/>
    </bean>
</beans>

上面是一个最简单的案例,可根据自己的情况修改。

四、插件加载失败怎么办?

新手最容易遇到的就是插件加载失败的问题,造成这个问题,可能性有很多种,但主要集中在

  • plugin.spring.xml 配置有误,pentaho7.0带的spring版本是 4.3.2,但如果你使用了更新版本spring才有的配置项,就可能出错,另外,pentaho的插件似乎并不支持注解,如果你在使用注解,也很可能在运行后出现各种错误,建议在xml定义各种bean以及相关的注入。
  • 依赖的jar包和pentaho本身有冲突,这个很好理解。
  • 使用Jersey有误。制作Pentaho的插件,如果涉及web的api,需使用JAXB标准。但貌似在pentaho的执行过程中,有时候会有一些莫名的bug,比如,当JAXB要上传一个文件时,需使用 FormDataParam,但如果在上传文件的同时,需要上传其他参数,这时候如果同时使用 FormDataParam ,就会发生错误,需要其他参数使用 QueryParam 才可以。

五、总结

pentaho的插件,在目前看来,更适合做一些功能单一,不太复杂的东西,如果涉及到太多数据库的各种增删改查,甚至有流程性,管理性的需求,就不太合适了。如果是有复杂的业务流程,个人的解决方案是,独立研发一套软件,结合实际的业务流程,部分涉及权限等的功能点也可利用pentaho的api接口,独立部署运行,会更合适一些,便于开发调试运行,开发成本会更低一些。事实上,本文很多内容,归根到底,就是JAXB的标准+spring4.3.2的结合。个人对pentaho也是刚接触,踩了不少坑,也还没细细研读pentaho的源码,可能本文也有不少缺漏及错误,但大体上是没有问题的,仅供参考,如有错误,烦请指出,承蒙指教,不胜感激。

版权声明:fenixadar 版权所有,如有转载,请注明出处,谢谢 https://blog.csdn.net/liaoyuanyue2004/article/details/84996033
 类似资料: