@Value,Spring,static-argument

遇到的问题:

    工作中遇到的情况,这次是要在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的临界值
  • 写好发送emailmethod

@Values annotation 赋值

对于各个参数的赋值按说是没有难度的,但是我这次不想hardcode这些参数,而是用properties文件以及@Values annotation来赋值,这样程序会更加灵活。

关于如何使用@Values以及Spring来把properties中的value注入到Bean中,详见这篇文章

按说跟着步骤来没什么难度,但实际实施起来还是出现了两个问题:

  • 自己不是太清楚classpath到底是指什么,classpathclasspath*有什么区别;
  • @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首行缩进:

&ensp;&ensp;&ensp;&ensp;这是段落1 &emsp;&emsp;&emsp;&emsp;这是段落2 &nbsp;&nbsp;&nbsp;&nbsp;这是段落3 无段落

效果如下:

    这是段落1     这是段落2     这是段落3 无段落