Overview
PhosphoPrediction
项目是Chris
和我做的一个新项目,主要是为本地客户端程序添加一个相同功能的web server
。由于出差新疆,只能晚上回酒店自己加班写代码,在Chris
的帮助下,前前后后忙了大约两周总算有了个不错的小成果,心中颇感欣慰。这段时间,Chris
不仅给了我技术上的指导,更给我排解了心中的许多烦恼,在此感谢我最好的朋友Chris
(我知道你不喜欢我当面夸奖感谢你,哈哈)。
1.Java web
服务器实现的功能
1.1 前端接收序列传入后台
很多类似的项目一次只能接收一条序列,本项目可以同时接收多条序列,包含名字的FASTA
格式的序列和不包含名字的raw
序列。在webserver
界面可以选择model
,默认为ATM
;同时可以选择threshold
,默认全部显示。
1.2 后台处理序列,并展示在jsp页面
后台共接收3
个参数,即seqString
、model
、threshold
,并生成getters
和setters
。
private String seqString;
private String model;
private double threshold;
public String getSeqString() {
return seqString;
}
public void setSeqString(String seqString) {
this.seqString = seqString;
}
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
public double getThreshold() {
return threshold;
}
public void setThreshold(double threshold) {
this.threshold = threshold;
}
我们在前端得到的threshold
,仅仅是double
类型的value
,我们处理成String
类型并展示在result
界面。我们可以根据选择的阈值threshold
,展示我们所需要的结果。为此,我们将处理result
的模块剥离出来,放在formatResults.java
类中。该类提供两个方法,是重载关系,唯一的区别是传入的参数threshold
。
//不包含threshold的方法,得到全部的sites对应的results
public ArrayList<Result> getFormatResults(String originSeqString, String model) {
RemoteWork robot = new RemoteWork();
ArrayList<Result> results = robot.formatInput(originSeqString);
ArrayList<Result> allResults = new ArrayList<>();
String seq_name="";
String seq="";
int input_count=1;
String input_replace="";
Result res;
for(int i=0; i<results.size(); i++){
res=results.get(i);
if(res.getName().substring(0, 5).equals("input")) {
input_replace = "-" + input_count;
res.setName(res.getName().replaceAll("-input", input_replace)+" *");
input_count++;
}
seq_name = res.getName();
seq = res.getSeq();
res.setDiso(robot.runDiso(seq));
res.setSs(robot.runSable_ss(seq));
res.setSa(robot.runSable_sa(seq));
robot.getFunc(seq_name);
res.setSites(robot.Predict(seq, model, seq_name));
allResults.add(res);
}
return allResults;
}
//包含threshold的方法,得到筛选的sites对应的results
public ArrayList<Result> getFormatResults(String originSeqString, String originModel, double originThreshold) {
ArrayList<Result> allResults = getFormatResults(originSeqString, originModel);
ArrayList<Result> formatResults = new ArrayList<>();
PredictionSite site=new PredictionSite();
for(Result result:allResults) {
ArrayList<PredictionSite> selectedPredictionSites = new ArrayList<>();
for(int j=0; j<result.getSites().size(); j++) {
site=result.getSites().get(j);
double d=Double.valueOf(site.getProb());
if(originThreshold==0) {
selectedPredictionSites.add(site);
}
else if(originThreshold==0.3){
if(!(d<0.3)){
selectedPredictionSites.add(site);
}
}
else if(originThreshold==0.5){
if(!(d<0.5)){
selectedPredictionSites.add(site);
}
}
else if(originThreshold==0.8){
if(!(d<0.8)){
selectedPredictionSites.add(site);
}
}
}
result.setSites(selectedPredictionSites);
formatResults.add(result);
}
return formatResults;
}
在action
中得到formatResults
后,在jsp
页面中循环,这里用到了jstl
表达式,需要引入包jstl.jar
,可以点击这里下载。jsp
页面的头部加上这么一句:
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<c:forEach items="${formatResults }" var="res">
<b> ${res.getName() } </b>
<c:forEach items="${res.getSites() }" var="site">
<c:out value="${site.getRank() }"/>
<c:out value="${site.getPos() }"/>
<c:out value="${site.getLeft().substring(0, 4) }"/></span><span class="emphasisLetter"> ${site.getLeft().substring(4) } </span><span class="constant"><c:out value="${site.getRight() }"/></span>
<c:out value="${site.getProb() }"/>
</c:forEach>
然后显示出Original Sequence
、Native Disorder
、Secondary Structure
、Solvent Accessibility
。
<c:out value="${res.getSeq() }"/>
<c:out value="${res.getDiso() }"/>
<c:out value="${res.getSs() }"/>
<c:out value="${res.getSa() }"/>
</c:forEach>
这里面JSTL
的详细用法,可参考JSTL标签库学习总结。
1.3 jsp页面同时添加导出结果按钮
添加导出为excel
和txt
两个功能的按钮。不在服务器端生成文件,而在客户端在线实时生成,节约服务器资源。
//结果写入excel文件
public InputStream getExcelFile() {
HttpSession session = ServletActionContext.getRequest().getSession();
formatResults=(ArrayList<Result>)session.getAttribute("formatResults");
HSSFWorkbook workbook = new HSSFWorkbook();
HSSFSheet sheet = workbook.createSheet("sheet1");
Result result;
PredictionSite site;
int k=0;
for(int i=0; i<formatResults.size(); i++){
result=formatResults.get(i);
HSSFRow row = sheet.createRow(k++);
HSSFCell cell = row.createCell(0);
cell.setCellValue(result.getName());
row = sheet.createRow(k++);
cell = row.createCell(0);
cell.setCellValue("Rank");
cell = row.createCell(1);
cell.setCellValue("Position");
cell = row.createCell(2);
cell.setCellValue("Surrounding Sequence");
cell = row.createCell(3);
cell.setCellValue("Probability Score");
for( int j=0;j<result.getSites().size();j++ ){
site=result.getSites().get(j);
row = sheet.createRow(k++);
cell = row.createCell(0);
cell.setCellValue(site.getRank());
cell = row.createCell(1);
cell.setCellValue(site.getPos());
cell = row.createCell(2);
cell.setCellValue(site.getLeft()+site.getRight());
cell = row.createCell(3);
cell.setCellValue(site.getProb());
}
row=sheet.createRow(k++);
cell = row.createCell(0);
cell.setCellValue("Original Sequence:");
row=sheet.createRow(k++);
cell = row.createCell(0);
cell.setCellValue(result.getSeq());
row=sheet.createRow(k++);
cell = row.createCell(0);
cell.setCellValue("Native Disorder:");
row=sheet.createRow(k++);
cell = row.createCell(0);
cell.setCellValue(result.getDiso());
row=sheet.createRow(k++);
cell = row.createCell(0);
cell.setCellValue("Secondary Structure:");
row=sheet.createRow(k++);
cell = row.createCell(0);
cell.setCellValue(result.getSs());
row=sheet.createRow(k++);
cell = row.createCell(0);
cell.setCellValue("Solvent Accessibility:");
row=sheet.createRow(k++);
cell = row.createCell(0);
cell.setCellValue(result.getSa());
//换行,打印空行
row=sheet.createRow(k++);
cell = row.createCell(0);
cell.setCellValue("\n");
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
workbook.write(baos);
} catch (IOException e) {
e.printStackTrace();
}
byte[] ba = baos.toByteArray();
ByteArrayInputStream bais = new ByteArrayInputStream(ba);
return bais;
}
public String downloadExcel() throws Exception{
return SUCCESS;
}
输出excel
文件的模块,主要参考了在线实时生成Excel文件流供下载。但是这篇博客写的太繁琐,于是将它简化了一大部分。这其中需要用到一个包:poi-3.13-20150929.jar
,最新版本的poi
可以在这里下载。更加详细的POI
操作excel
的方法,请参考这里。
而导出txt
文件的方法则要简单许多。
public InputStream getTxtFile(){
HttpSession session = ServletActionContext.getRequest().getSession();
formatResults=(ArrayList<Result>)session.getAttribute("formatResults");
Result result;
PredictionSite site;
StringBuffer sb=new StringBuffer();
String newLine,tab,str0,str1,str2,str3,str4,str5,str6,str7,str8,
str9,str10,str11,str12,str13,str14,str15,str16;
newLine="\n";
tab="\t";
for(int i=0; i<formatResults.size(); i++){
result=formatResults.get(i);
str0=result.getName();
sb.append(str0).append(newLine);
str1="Rank";
str2="Position";
str3="Surrounding Sequence";
str4="Probability Score";
sb.append(str1).append(tab).append(str2).append(tab).append(str3).append(tab).append(str4).append(tab).append(newLine);
for( int j=0;j<result.getSites().size();j++ ){
site=result.getSites().get(j);
str5=site.getRank();
str6=site.getPos();
str7=(site.getLeft()+site.getRight());
str8=site.getProb();
sb.append(str5).append(tab).append(str6).append(tab).append(str7).append(tab).append(str8).append(tab).append(newLine);
}
str9="Original Sequence:";
str10=result.getSeq();
str11="Native Disorder:";
str12=result.getDiso();
str13="Secondary Structure:";
str14=result.getSs();
str15="Solvent Accessibility:";
str16=result.getSa();
sb.append(str9).append(newLine).append(str10).append(newLine).append(str11).append(newLine).append(str12).append(newLine)
.append(str13).append(newLine).append(str14).append(newLine).append(str15).append(newLine).append(str16).append(newLine).append(newLine);
}
ByteArrayInputStream in = new ByteArrayInputStream(sb.toString().getBytes());
return in;
}
public String downloadTxt() throws Exception{
return SUCCESS;
}
2.需要注意的地方
这次的项目,过程中出现过几次低级错误。Chris
说:“越是奇葩的bug,往往隐藏着低级的错误。”这句话在这个项目中屡屡应验。记录一下,以避免再犯。
2.1 struts.xml
文件配置
本项目共产生3
个action
:webserver
界面点击提交,excel
文件导出,txt
文件导出。配置如下
<action name="result" class="jphospho.JPhospho" method="execute">
<result>
/serverResult.jsp
</result>
</action>
<action name="excel" class="jphospho.JPhospho" method="downloadExcel">
<result type="stream">
<param name="contentType">application/vnd.ms-excel</param>
<param name="contentDisposition">attachment;fileName="Detail.xls"</param>
<param name="inputName">excelFile</param>
<param name="bufferSize">1024</param>
</result>
</action>
<action name="txt" class="jphospho.JPhospho" method="downloadTxt">
<result type="stream">
<param name="contentType">application/octet-stream</param>
<param name="inputName">txtFile</param>
<param name="contentDisposition">attachment;fileName="Detail.txt"</param>
<param name="bufferSize">4096</param>
</result>
</action>
需要注意的是,第一个action
的result type
不能是chain
,如果是chain
,则会出现不跳转而直接下载文件的bug
。那么怎么在action
之间进行传值呢?详见下一条。
2.2 action
之间传值
配置struts.xml
是行不通的,我们采用了HttpSession
传值方法。在第一个action
中的return SUCCESS
之前,加上这么两句代码
HttpSession session = ServletActionContext.getRequest().getSession();
session.setAttribute("formatResults", formatResults);
这样,formatResults
就保存在了session
中,然后可以在另外两个action
中取值了。
HttpSession session = ServletActionContext.getRequest().getSession();
formatResults=(ArrayList<Result>)session.getAttribute("formatResults");
2.3 处理输入的序列
输入序列如果不包含名字,是多条裸序列,那么原来的项目会当做是一条序列处理,这显然是不符合要求的。我们为每一个序列之前添加一个>i
,i
从1
开始计数。在这里,处理换行符的时候,有一点要特别注意。接收的字符串中的换行符,一定是\r\n
,而输出的文件中,换行需要System.getProperty("line.separator")
,否则就会出现问题。
public String getFormatSeqString(String originSeqString) {
String formatSeqString;
boolean b = String.valueOf(originSeqString.trim().charAt(0)).equals(">");
if(b) {
formatSeqString = originSeqString;
return formatSeqString;
}
String[] strs = originSeqString.trim().split("\r\n");
ArrayList<String> list = new ArrayList<>();
int k=1;
for(int i=0; i<strs.length; i++){
if(!strs[i].equals("") && !strs[i].equals("\r\n")){
list.add(">"+(k++)+"\r\n"+strs[i]);
}
}
formatSeqString=String.join("\r\n",list);
return formatSeqString;
}
2.4 初始化问题
在生成文件方法里,StringBuffer
必须得用new初始化,否则会出现空指针错误。
StringBuffer sb=new StringBuffer();
关于String
,StringBuffer
和StringBuilder
的区别可以参考这里。StringBuffer
是可扩展且线程安全的,因此,我们用它最好。
另外,多重循环的初始化一定要注意初始化的位置。