Overview1.修改hosts文件2.设置eclipse Overview 前面讲到，消息队列需要用到客户端的ip地址和时间戳，获取客户端的ip地址就需要下面几行简单的代码。 import javax.servlet.http.HttpServletRequest; HttpServletRequest request = ServletActionContext.getRequest(); String ipAddress = request.getHeader("X-FORWARDED-FOR"); if (ipAddress == null) { ipAddress = request.getRemoteAddr(); } System.out.println(ipAddress); 我将上面几行代码，放入之前的PhosphoPredict项目的action中做实验，结果显示如下： 0:0:0:0:0:0:0:1 这和我们平时的认知不同，下面就是解决办法。 1.修改hosts文件 我在ubuntu14.04系统下，打开/etc/hosts文件，看到如下信息： # Localhost 127.0.0.1 localhost 127.0.0.1 young 255.255.255.255 broadcasthost ::1 localhost fe80::1%lo0 localhost 我们将 ::1 localhost 这句注释掉，再提交， 127.0.0.1 正确答案就出现了。 其他系统，请自行查找文件目录并修改。 2.设置eclipse 我们在eclipse运行本程序的时候，可以配置一下运行参数： 依次点击Run→Run Configurations…→Apache Tomcat→Tomcat v7.0 Server at localhost,这时候点击右边的Arguments，在下面的VM arguments框中加上一句： -Djava.net.preferIPv4Stack=true 最后点击Apply。然后就可以了。 总结：这是因为，只有在本机提交的时候，才会出现，客户端提交就会显示真实ip地址了。而系统会优先显示ipv6格式的本机ip。我们两种方法，第一种是屏蔽掉ipv6的表示方法，第二种则是指定优先ipv4表示。 两种方法更推荐第二种。 这篇文章主要参考了stackoverflow.
Overview1.Java web服务器实现的功能1.1 前端接收序列传入后台1.2 后台处理序列，并展示在jsp页面1.3 jsp页面同时添加导出结果按钮2.需要注意的地方2.1 struts.xml文件配置2.2 action之间传值2.3 处理输入的序列2.4 初始化问题 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 getFormatResults(String originSeqString, String model) { RemoteWork robot = new RemoteWork(); ArrayList results = robot.formatInput(originSeqString); ArrayList 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 getFormatResults(String originSeqString, String originModel, double originThreshold) { ArrayList allResults = getFormatResults(originSeqString, originModel); ArrayList 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"> ${res.getName() } <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) }"/> ${site.getLeft().substring(4) } <c:out value="${site.getRight() }"/> <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 )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 )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文件导出。配置如下 /serverResult.jsp application/vnd.ms-excel attachment;fileName="Detail.xls" excelFile 1024 application/octet-stream txtFile attachment;fileName="Detail.txt" 4096 需要注意的是，第一个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 )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是可扩展且线程安全的，因此，我们用它最好。 另外，多重循环的初始化一定要注意初始化的位置。
Overview1.直接运行Shell命令2.执行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脚本文件 Runtime的exec()方法还有另外一种重载形式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("你的工作目录的路径") 。
Overview Weka is easy to use when we do feature selection and classification. Using Weka's friendly graphic user interface, we can try and validate multiple feature selection methods and classifiers that have already been implemented into Weka. This article tries to give a quick start guide about this wonderful tool. 1. Download Weka 1.1 About different versions You can download Weka with different versions from Weka's Download page. There are Stable book 3rd ed. version(3.6.12) and Developer version(3.7.12+) available. Stable book 3rd ed. version is a stable version for use. If this version can meet all your demands or you don't know what functions you need, use this one. Compared to the former versions of Weka, Developer version(3.7.12+) adopts a new architecture, which provides a package manager in the tool menu. Unlike that Stable book 3rd ed. version(3.6.12) provides all the methods and classifier it can, Developer version(3.7.12+) only provides a series of basic methods and classifiers in default, however, with a new-developed package manager. If you want some classifier, open the package manager and download this classifier, then you can use it in Weka. Taking feature selection as an instance, we want to use mRMR method to select features. Fortunately, mRMR is involved in weka at version 3.5.15. We don't need to update a new version since we only need this function. Open the package manager in the existing 3.7.12 version, we can easily download and use it. 1.2 For users in different platforms Weka provides a series of versions that can run on Windows, Mac and other platforms(a zip file). For Windows and Mac users, you can directly download the corresponding version, install it and use it. However, I recommend you to download a zip version to use. When you wanna use SVM classifier in Weka 3.6.12, you will need the zip version. (How to involve libSVM in weka 3.6.12 will be depicted in another article) 2. Use Weka If you download the non-zip version, I guess you are already using Weka. So we just talk about how to use the zip version. 2.1 Run Weka Enter the weka folder, and use the following command to start it: java -Xmx1000M -jar weka.jar Notice the parameter -Xmx1000M. This means Weka can use a maximum of 1000M memory to use. When you wanna training big data set, set a bigger value will prevent the breakdown of Weka(I use 4000M memory). 2.1 Run Weka anywhere We have already run Weka. There is still a problem: Every time we wanna use Weka, we have to enter the Weka folder and run the start command. By setting the environmental viable, we can tackle this problem. The setting in my computer is as follows: CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib:/Users/wangjiawei/weka/weka-3-6-12/weka.jar /Users/wangjiawei/weka/weka-3-6/ is the folder of Weka. After adding the folder address into CLASHPATH, we can use the start command at any place.
Overview1.FileTimeStamp获取包含时间戳的文件名2.建立一个调用FileTimeStamp类的FileFullName类 Overview 第一个项目中，输入框内的sequence传入后台，并在Action中用String类型的seq接收，继而处理成txt文件形式保存。由于以后的项目均要频繁使用io以及这种中间处理方式，故在参考了前人的类似处理方法之后，提取出适合本项目的Java类FileTimeStamp。 该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; } } 参考资料
Java日志体系概览 关于Java的日志API可以概括为日志记录的封装API和日志记录实现两类。前一种的典型代表是Apache Commons Logging和SLF4J，后一种的典型代表有JDK自带的日志实现（java.util.logging 包，JUL）以及著名的Log4j。日志封装API是为日志使用者提供了一种统一的接口，使用者可以根据需求来切换具体日志实现方案。 Java日志API 一般来说，日志API一般包括： 记录器（Logger）：日志 API 的使用者通过记录器来发出日志记录请求，并提供日志的内容。在记录日志时，需要指定日志的严重性级别。 格式化器（Formatter）：对记录器所记录的文本进行格式化，并添加额外的元数据。 处理器（Handler）：把经过格式化之后的日志记录输出到不同的地方。常见的日志输出目标包括控制台、文件和数据库等。 Java日志封装API 在这主要介绍一下目前使用较多的SLF4J日志库，SLF4J 库中核心的 API 是提供工厂方法的 org.slf4j.LoggerFactory 类和记录日志的 org.slf4j.Logger 接口。通过 LoggerFactory 类的 getLogger 方法来获取日志记录器对象。Logger 接口中的方法也是按照不同的严重性级别来进行分组的。 参考：Java日志最佳实践 Jeesite中Log4j的使用分析 Jeesite中使用了Log4j日志记录作为实现，下面依次分析一下Log4j的配置文件和封装了Log4j的Manager类。 Log4j的配置 Log4j支持使用Java properties和xml作为配置文件。相对于xml的格式，Java properties更易读。下面以Jeesite中的log4j.properties为例，所有配置项均以log4j开头，主要配置项为rootlogger,输出终端，输出布局模式。 log4j.rootLogger=WARN, Console, RollingFile Logger是日志记录器，而rootLogger则是所有Logger的祖先，可以通过Logger.getRootLogger()来获取。 #Console log4j.appender.Console=org.apache.log4j.ConsoleAppender log4j.appender.Console.layout=org.apache.log4j.PatternLayout log4j.appender.Console.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n #RollingFile log4j.appender.RollingFile=org.apache.log4j.DailyRollingFileAppender log4j.appender.RollingFile.File=../logs/jeesite.log log4j.appender.RollingFile.layout=org.apache.log4j.PatternLayout log4j.appender.RollingFile.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n Log4j的输出终端，由Appender接口定义。其有多种实现，不同实现就对应则不同的日志输出保存方式。例如： ConsoleAppender(控制台) FileAppender(文件) DailyRollingFileAppender(每天都产生一个日志文件) RollingFileAppender(文件大小达到指定尺寸时产生一个新的日志文件，文件名称上会自动添加数字序号。) WriterAppender(将日志信息以流的格式发送到任意指定的地方) 而所有子类Logger均将继承父类Logger的Appender配置。 至于输出布局模式——由Layout接口定义——也有多种实现： PatternLayout（可以灵活地指定布局模式） HTMLLayout（以HTML表格形式布局） SimpleLayout（包含日志信息的级别和信息字符串） TTCCLayout（包含日志产生的时间、线程、类别等信息） ConversionPattern使用的是类似于C语言中printf的格式化输出参数： %c：输出所属的类目，通常就是所在类的全名。 %d：输出日志时间点的日期或时间，默认格式为ISO8601，推荐使用“%d{ABSOLUTE}” %t：输出产生该日志线程的线程名。 %p：输出优先级。 %m：输出代码中指定的消息。 %n：输出一个回车换行符。Windows平台为“\r\n”，UNIX为“\n”。 所以jeesite中的日志输出格式为： Output pattern : date [thread] priority category - message FATAL 0 ERROR 3 WARN 4 INFO 6 DEBUG 7 每个Logger都有一个日志记录级别，对应不同程度的信息，按照优先级可以排列如下：ALL<DEBUG<INFO<WARN<ERROR<FATAL<OFF #Hibernate level #log4j.logger.org.hibernate=ERROR log4j.logger.org.hibernate.cache.ehcache.AbstractEhcacheRegionFactory=ERROR log4j.logger.org.hibernate.search.impl.ConfigContext=ERROR log4j.logger.net.sf.ehcache.config.CacheConfiguration=ERROR #Project defalult level log4j.logger.com.thinkgem.jeesite=DEBUG 这里分别配置了Hibernate操作数据库的日志记录级别和项目的默认日志记录级别，均为DEBUG,即与程序运行时的流程相关的详细信息。 Log4jManager类 Log4jManager类对于Log4j，主要是对于Log4j的日志记录级别进行管理配置。使用了叫JMX（Java Management Extensions，即Java管理扩展）——一个为应用程序、设备、系统等植入管理功能的框架——对日志记录级别进行管理。此处把Log4j作为Mbean（管理Bean组件）来管理。首先来看一些Getters/Setters函数 @ManagedAttribute(description = "Level of the root logger") public String getRootLoggerLevel() { Logger logger = Logger.getRootLogger(); return logger.getEffectiveLevel().toString(); } @ManagedAttribute public void setRootLoggerLevel(String newLevel) { Logger logger = Logger.getRootLogger(); Level level = Level.toLevel(newLevel); logger.setLevel(level); managerLogger.info("设置Root Logger 级别为{}", newLevel); } 这是对根日志记录器进行日志级别设置和获取。managerLogger对象是通过LoggerFactory.getLogger(Log4jManager.class)这一日志记录器工厂来获取的。 @ManagedOperation(description = "Get logging level of the logger") @ManagedOperationParameters({ @ManagedOperationParameter(name = "loggerName", description = "Logger name") }) public String getLoggerLevel(String loggerName) { Logger logger = Logger.getLogger(loggerName); return logger.getEffectiveLevel().toString(); } /** * 设置Logger的日志级别. * 如果日志级别名称错误, 设为DEBUG. */ @ManagedOperation(description = "Set new logging level to the logger") @ManagedOperationParameters({ @ManagedOperationParameter(name = "loggerName", description = "Logger name"), @ManagedOperationParameter(name = "newlevel", description = "New level") }) public void setLoggerLevel(String loggerName, String newLevel) { Logger logger = Logger.getLogger(loggerName); Level level = Level.toLevel(newLevel); logger.setLevel(level); managerLogger.info("设置{}级别为{}", loggerName, newLevel); } 同根Logger一样，此处的普通Logger和项目级别的Logger的Setter\Getter方法，也是大同小异的。 SYS模块中的Log部分 这部分的日志log，并未使用日志记录API，而是通过正常模块开发的形式，把一些操作信息存入数据库表（sys_log）中，其保存的信息包含： private String id; // 日志编号 private String type; // 日志类型（1：接入日志；2：错误日志） private User createBy; // 创建者 private Date createDate; // 日志创建时间 private String remoteAddr; // 操作用户的IP地址 private String requestUri; // 操作的URI private String method; // 操作的方式 private String params; // 操作提交的数据 private String userAgent; // 操作用户代理信息 private String exception; // 异常信息 日志查询可以在登录后台系统后，通过host/sys/log这个url来访问。
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:site， javadoc 可以找到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) 这样的小技巧，否则，这里不能识别这种写法。 保存，并重启电脑，配置生效。