遇到的问题:
工作中遇到的情况,这次是要在billing-matrix包中的monitor加一个scheduler的feature,这个包也是采用MVC框架,mirror server上跑着这个程序, 访问http://54.203.255.103:8080/monitor/show.do便可检查Production Server的响应速度。而我要做的是统计一下这几项值的大致范围,选择出一个临界点,超过临界点就发邮件通知。原系统中已经有一个scheduler的程序,会周期性的把monitorResult.toString( )
track到log中。而这个monitorResult
又含有我需要监测的那些field
,所以我要做的事情就比较简单了:
- 确定各个
field
的临界值 - 写好发送
email
的method
@Values
annotation 赋值
对于各个参数的赋值按说是没有难度的,但是我这次不想hardcode
这些参数,而是用properties
文件以及@Values annotation
来赋值,这样程序会更加灵活。
关于如何使用@Values
以及Spring来把properties中的value注入到Bean中,详见这篇文章。
按说跟着步骤来没什么难度,但实际实施起来还是出现了两个问题:
- 自己不是太清楚classpath到底是指什么,
classpath
与classpath*
有什么区别; - @Values总是注入不近来,导致NullPointerException.
关于第一个问题,我引用[这里](http://hi.baidu.com/huahua035/item/ac8a27a994b55bad29ce9d39)的解释:
web.xml中classpath是什么含义 简单理解,classpath就是代表 /WEB-INF /classes/ 这个路径(如果不理解该路径,就把一个web工程发布为war包,然后用winrar查看其包内路径就理解啦)
常用的场景: 在SSH架构中,配置Spring的上下文环境:
contextConfigLocation classpath:applicationContext.xml 里面的 classpath:applicationContext.xml
也可以使用 /WEB-INF /classes/ applicationContext.xml 代替注意: classpath 和 classpath 区别: classpath:只会到你的class路径中查找找文件; classpath:不仅包含class路径,还包括jar文件中(class路径)进行查找
关于第二个问题: 我最初的想法是想把username等field声明为static的,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | @Value("${monitor.email.username:#{null}}") static private String username; @Value("${monitor.email.password:#{null}}") static private String password; @Value("${monitor.email.fromemail:#{null}}") static private String fromEmail; @Value("${monitor.email.toemail.notserious:#{null}}") private String toEmailNotSerious; @Value("${monitor.email.toemail.serious:#{null}}") private String toEmailSerious; |
但是事实证明value注入不进来的原因就是static, 发现自己对于static的认识还是不够,下面引用stack overflow上别人的解答
static members belong to the class instead of a specific instance.
It means that only one instance of a static field exists even if you create a million instances of the class or you don't create any. It will be shared by all instances.
Since static methods also do not belong to a specific instance, they can't refer to instance members (how would you know which instance Hello class you want to refer to?). static members can only refer to static members. Instance members can, of course access static members.
Side note: Of course, static members can access instance members through an object reference.
但是没有找到非常适合这个case的解释,我觉得可能Spring在把这个value注入到Bean中时是属于instance范畴的,而不是calss,因为Bean是一个Object,臆测。。总之删掉static就可正常赋值
发送email
我的代码是这样的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | package com.fuhu.osg.billing.matrix.scheduler; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Queue; import java.util.concurrent.ArrayBlockingQueue; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import com.fuhu.osg.billing.matrix.data.json.MonitorObject; import com.fuhu.osg.billing.matrix.factory.AnalyzeFactory; import com.fuhu.osg.billing.matrix.util.DataUtil; import com.fuhu.osg.billing.matrix.util.FileUtil; import com.fuhu.osg.billing.restclient.MonitorRESTClient; import com.fuhu.osg.billing.result.MonitorResult; import com.fuhu.osg.util.Constants; import java.util.Properties; import javax.mail.Message; import javax.mail.MessagingException; import javax.mail.PasswordAuthentication; import javax.mail.Session; import javax.mail.Transport; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMessage; @Component public class MatrixScheduler { private static Logger logger = Logger.getLogger(MatrixScheduler.class); private static final Map<String, Queue<MonitorResult>> serverMap = new HashMap<String, Queue<MonitorResult>>(); private static final List<String> serverList = new ArrayList<String>(); @Autowired private MonitorRESTClient client; @Value("${monitorEmailUsername}#{null}") private String username; @Value("${monitorEmailPassword}") private String password; @Value("${monitorFromEmail}") private String fromEmail; @Value("${monitorToEmailNotSerious}") private String toEmailNotSerious; @Value("${monitorToEmailSerious}") private String toEmailSerious; boolean serious; @Scheduled(cron = "${monitor.scheduler}") public void monitorActivated() { MonitorResult result = client.monitorStatus(); Queue<MonitorResult> queue = null; if (serverMap.get(result.getServerId()) == null) { queue = new ArrayBlockingQueue<MonitorResult>(48); serverMap.put(result.getServerId(), queue); serverList.add(result.getServerId()); } else { queue = serverMap.get(result.getServerId()); } if (queue.size() == 48) { queue.poll(); } if (result.getStatus().equals(Constants.SUCCESS)) { queue.add(result); } else { queue.add(new MonitorResult()); } logger.trace(result.toString()); // for data analysis if (result.getTotalRTT() != 0.0) { System.out.println("Result is --> " + result.toString()); } StringBuilder sBuilder = new StringBuilder(); if (result.getCoppaRTT() > 500) { // send email String content = "CoppaRTT is above 500"; serious = false; sendEmail(content, username, password, fromEmail, toEmailNotSerious, toEmailSerious); } } public static Queue<MonitorResult> getDataQueue(String serverId) { return serverMap.get(serverId); } public static List<String> getServerList() { return serverList; } // inner class uses username & password, so use final parameter. public void sendEmail(String content, final String username, final String password, String fromEmail, String toEmailNotSerious , String toEmailSerious) { Properties props = new Properties(); props.put("mail.smtp.host", "smtp.gmail.com"); props.put("mail.smtp.socketFactory.port", "465"); props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory"); props.put("mail.smtp.auth", "true"); props.put("mail.smtp.port", "465"); Session session = Session.getDefaultInstance(props, new javax.mail.Authenticator() { protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication(username, password); } }); try { Message message = new MimeMessage(session); message.setFrom(new InternetAddress(fromEmail)); if (serious) { message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(toEmailSerious)); } else { message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(toEmailNotSerious)); } message.setSubject("Server Monitor Warning"); message.setText("Attention, server in danger " + content); Transport.send(message); } catch (MessagingException e) { throw new RuntimeException(e); } } } |
这里sendEmail()有一点tricky的地方,就是用了final variable
, final String username
& final String password
, 这是因为sendEmail()method中的inner class
使用了这两个argument
. 原因:
There are two main reasons you might want to mark an argument final. First, if you're planning on using the argument in an anonymous inner class, then you must mark it final so that it can be referenced in that class. This is actually a pretty common use case for marking arguments final.
The other common reason to mark arguments final is to prevent yourself from accidentally overwriting them. If you really don't want to change the arguments, then perhaps you should mark them final so that if you actually do, you'll get the error at compile-time rather than finding out at runtime that your code has a bug.
详见stack overflow的解释。
Markdown首行缩进:
    这是段落1
    这是段落2
这是段落3
无段落
效果如下:
这是段落1 这是段落2 这是段落3 无段落