您正在查看: 后台开发 分类下的文章

Java程序调用Shell命令及脚本文件

Overview

最近需要用到数个Python程序处理蛋白质序列以输出特征值,而这些Python文件需要在Shell脚本中传入文本文件(该文本文件记录了某些蛋白质序列)做参数,进而依次被Shell调用。我们在Java程序中建立Shell脚本的运行时环境Runtime,这其中用到了一个类,即java.lang.Runtime,下面对该类进行探讨和记录。

1.直接运行Shell命令

java.lang.Runtime类有一个特点,官方文档说明如下:

Every Java application has a single instance of class Runtime that allows the application to interface with the environment in which the application is running. The current runtime can be obtained from the getRuntime method.
An application cannot create its own instance of this class.

大意就是:Java程序不能实例化(所以也没有构造方法),如需获取当前的运行环境只能通过无参的getRuntime()方法,该方法返回值为Runtime,进而使用它的exec(String command)或者exec(String[] cmdarray),返回一个Process。代码如下:

Process process;
List<String> processList = new ArrayList<String>();  
try {  
    process = Runtime.getRuntime().exec(“pwd”);
    BufferedReader input = new BufferedReader(new InputStreamReader(process.getInputStream()));  
    String line = "";  
    while ((line = input.readLine()) != null) {  
        processList.add(line);  
    }  
    input.close();  
} catch (IOException e) {  
    e.printStackTrace();  
}  
for (String line : processList) {  
    System.out.println(line);  
} 

"pwd"命令运用之后,帮助我们得到当前的工作目录为/root,从而为下一步执行Shell脚本文件也提供了有利条件。

2.执行Shell脚本文件

Runtimeexec()方法还有另外一种重载形式exec(String[] cmdarray, String[] envp, File dir),其作用更为强大,开发人员可以将命令全部写入脚本文件,使得命令连续执行,而且可以指定脚本文件的工作目录。

public void read(String str) throws NullPointerException{  
                            
    String command = "/bin/bash" + " " + ServletActionContext.getServletContext().getRealPath("/") + "useful_scripts/feature_calc.sh"+" "+str;
    File dir = new File(ServletActionContext.getServletContext().getRealPath("/") + "useful_scripts");
    System.out.println(command);//测试command命令
    Process process;
    List<String> processList = new ArrayList<String>();  
    try {  
        process = Runtime.getRuntime().exec(command, null, dir);//环境变量通常为null,表明使用当前环境。
        BufferedReader input = new BufferedReader(new InputStreamReader(process.getInputStream()));  
        String line = "";  
        while ((line = input.readLine()) != null) {  
            processList.add(line);  
        }  
        input.close();  
    } catch (IOException e) {  
        e.printStackTrace();  
    }  
  
    for (String line : processList) {  
        System.out.println(line);  
    }  
}

其中有一点需要注意:dir参数类型是File,但是官方文档解释为“工作目录”,故用法如下:

File dir = new File("你的工作目录的路径")

Java处理文件名加时间戳

Overview

第一个项目中,输入框内的sequence传入后台,并在Action中用String类型的seq接收,继而处理成txt文件形式保存。由于以后的项目均要频繁使用io以及这种中间处理方式,故在参考了前人的类似处理方法之后,提取出适合本项目的JavaFileTimeStamp
Java类负责以“sequence_input_时间戳”的形式命名文本文件。以下便是该处理类的代码,而参考资料也会在最后给出,以尊重原作者。

1.FileTimeStamp获取包含时间戳的文件名

import java.text.SimpleDateFormat;
import java.util.Date;

public class FileTimeStamp {
    private SimpleDateFormat sdf = null;
    
    //获取时间戳
    public String getTimeStamp(){
        sdf = new SimpleDateFormat("yyyyMMddHHmmssZ");
        String timeStamp = sdf.format(new Date());
        return timeStamp;
    }
    
    //获取添加了时间戳和扩展名的文件名
    //并将StringBuffer类型的buf转换为字符串
    //于是便得到完整的文件名
    public String getTimeName(){
        StringBuffer buf = new StringBuffer("sequence_input_");
        buf.append(this.getTimeStamp()).append(".txt");
        return buf.toString();
    }
}

2.建立一个调用FileTimeStamp类的FileFullName

该类负责产生文件全名,并在后面运用。

package edu.monash.file;

public class FileFullName {
    public String getFullName() {
        FileTimeStamp fts = new FileTimeStamp();
        String fullName = fts.getTimeName();
        return fullName;
    }
}

参考资料

关于代码生成器的使用和解析

代码生成器的使用

正如许多J2EE整合开发框架一样,Jeesite也提供了代码生成器,我们可以通过配置,利用它来生成一个独立模块的各层次的基础部分,其中包括:

  • Entity,即实体类。对应MVC的Model部分。其继承了Common模块中的模板类BaseEntity。
  • DAO层,封装了对实体类的CRUD操作,相应地继承了模板类BaseDAO。
  • Service层,封装底层,并加了一些基础功能,作为服务层。
  • Controller层,对应MVC的C部分。相应继承了抽象类BaseController。
  • JSP,对应MVC的View部分。代码生成器会生成xxxList和xxxForm两个JSP页面,分别用以展示和增删改查数据条目。此处的XXX为类名。

代码生成器的使用非常简单,首先找到com.thinkgem.jeesite.generate包下的generate类。首先根据注释,自行定义新模块的包名、模块名、类名、功能名、类作者名的对应变量。其中:

  • 包名.模块名.是生成的新模块各个层次的包,默认会生成entity、dao、service、web四个包。而WEB-INF/views/modules/下会生成一个模块名的文件夹。该文件夹为上面提到的两个JSP页面。
  • 类名就对应四个包下类名前缀,若类名为Sign,则四个包下的四个类分别为Sign、SignDAO、SignService、SignController。
  • 功能名是为了说明功能模块的作用,会在生成的JSP中包含对应信息。而类作者名则会加到生成类的注释中。
    配置好这些后,对generate类选择RUN AS - Java Application即可。由于该类包含Main()方法,是可执行的。然后它会读取com.thinkgem.jeesite.generate.template下的6个ftl文件——这是freemaker模板引擎使用的文件——最后填入对应的配置变量,生成如前所述的4个类和2个JSP页面。

最后要运行这个生成模块的话,还需手动在数据库中建立对应的数据表,数据表除了在实体类中定义的属性外,还必须包含del_flag、remarks、create_by、create_date、update_by、update_date这几个字段。这是因为实体类要继承DataEntity这个抽象类,此类中定义了这些属性,后续的查询、排序中要用到这些字段。

代码生成器各部分解析

代码生成器实际为我们生成了MVC的各个层次,下面结合代码,分析一下各部分的原理。

Entity实体层

首先,任何Entity都是继承了IdEntity这个抽象类,IdEntity主要提供实体类的id属性及其对应setter和getter方法。这样生成的entity就继承了这个属性。关于com.thinkgem.jeesite.common.persistence包下几个基础Entity类的关系可以表示如下:IdEntity--->DataEntity--->BaseEntity。这样每个生成的Entity就继承了包括id、del_flag、remarks、create_by、create_date、update_by、update_date在内的属性。其中id是利用util中的IdGens生成的uuid,即生成的id不会自增。而是一段字母+数字的唯一组合。若对id有auto_increment的要求,则需要考虑重写IdEntity类。

Dao层

实际上,所有代码生成的dao都是直接继承了common中的BaseDao类,继承后的子类并未添加任何属性和方法。在此就主要针对BaseDao类分析。总的来说,BaseDao主要是在Hibernate提供的数据库操作基础上,封装了Page、parameter参数。这里的Page是作为展示页面的对象,而Parameter是继承了HashMap<String, object>的哈希字典。BaseDao就针对实体对象,提供了find、get、save、update等几类操作,每种操作根据参数的不同有几种函数重载形式。而对删除操作,这里是通过deleteById来设置del_flag字段的布尔值进行逻辑删除的。如果要进行真实的表中某行数据删除,需要我们自己写对应的操作函数。

Service层

新模块的Service层一般都有一个dao对象的属性,然后在dao的基础上进一步封装,加入一些其它有用功能的服务。由于不同模块提供的服务不尽相同,这里就不好进行概括了。而BaseService类则是提供了根据用户角色的DATA_SCOPE进行数据过滤。

Controller层

新建模块的Controller主要使用Spring提供的Annotation,把访问Url与实际的Jsp页面映射起来,与此同时使用Service、Dao对象提供的数据库操作函数,这样就可以在页面上对数据库进行增删改查。而不同Controller继承的BaseController则主要通过封装BeanValidator提供对服务端参数有效性验证。例如我们通过页面向数据库添加实体对象时,可以利用BaseController进行Bean的校验,验证通过后再保存对象。关于BeanValidator较为详细的介绍可以参考这里

Maven中自动生成关于项目站点的一些坑

Overview

主要使用使用Maven自动生成项目站点的一些坑。

List

  • maven-site-plugin 插件需要使用3.3版本,如果使用3.2,由于兼容性问题可能在site阶段报错。

  • Eclipse中启动mvn site阶段找不到JAVA_HOME:
    报错信息如下:

     Failed to execute goal org.apache.maven.plugins:maven-javadoc-plugin:2.9.1:jar (attach-javadocs) on project suppress-warnings: MavenReportException: Error while creating archive: Unable to find javadoc command: The environment variable JAVA_HOME is not correctly set.
    

    已经在本地的配置中设置了JAVA_HOME

     export JAVA_HOME=$(/usr/libexec/java_home)
    

    在命令行运行 mvn clean site:sitejavadoc 可以找到JAVA_HOME,但是在eclipse中执行maven构建,还是会报一样的错误,这是由于,在本地设置的JAVA_HOME不能被A bundled GUI app like Eclipse识别,需要在全局配置中添加该信息。
    找到/etc/launchd.conf文件,如果该目录不存在这个文件,sudo vim launchd.conf创建改文件,必须使用sudo,不然无法写入该文件,添加一行:

     setenv JAVA_HOME /Library/Java/JavaVirtualMachines/jdk1.8.0_25.jdk/Contents/Home
    

    注意:不能使用

     setenv JAVA_HOME $(/usr/libexec/java_home) 
    

    这样的小技巧,否则,这里不能识别这种写法。

    保存,并重启电脑,配置生效。