/*
* Copyright 2015 the original author or authors.
* @https://github.com/scouter-project/scouter
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package scouter.server.plugin.alert;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.CtNewMethod;
import javassist.NotFoundException;
import scouter.server.Configure;
import scouter.server.Logger;
import scouter.util.BitUtil;
import scouter.util.CastUtil;
import scouter.util.FileUtil;
import scouter.util.HashUtil;
import scouter.util.LongSet;
import scouter.util.StringEnumer;
import scouter.util.StringKeyLinkedMap;
import scouter.util.StringUtil;
import scouter.util.ThreadUtil;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FilenameFilter;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Properties;
public class AlertRuleLoader extends Thread {
private static AlertRuleLoader instance;
public synchronized static AlertRuleLoader getInstance() {
if (instance == null) {
instance = new AlertRuleLoader();
instance.setDaemon(true);
instance.setName("AlertRuleLoader");
instance.start();
}
return instance;
}
public StringKeyLinkedMap<AlertRule> alertRuleTable = new StringKeyLinkedMap<AlertRule>();
public StringKeyLinkedMap<AlertConf> alertConfTable = new StringKeyLinkedMap<AlertConf>();
public void run() {
while (true) {
ThreadUtil.sleep(5000);
try {
File root = new File(Configure.getInstance().plugin_dir);
if (root != null && root.canRead()) {
checkModified(root);
checkNewRule(root);
}
} catch (Throwable t) {
t.printStackTrace();
}
}
}
private void checkNewRule(File root) {
File[] ruleFiles = root.listFiles(new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.endsWith(".alert");
}
});
for (int i = 0; i < ruleFiles.length; i++) {
String name = getRuleName(ruleFiles[i].getName());
if (alertRuleTable.containsKey(name))
continue;
AlertRule rule = createRule(name, ruleFiles[i]);
if (rule == null)
continue;
AlertConf conf = createConf(name, getConfFile(ruleFiles[i]));
alertRuleTable.put(name, rule);
alertConfTable.put(name, conf);
}
}
private void clear(String name) {
alertRuleTable.remove(name);
alertConfTable.remove(name);
Logger.println("S217", "Clear alert rule : " + name);
}
private void checkModified(File root) {
StringEnumer en = alertRuleTable.keys();
while (en.hasMoreElements()) {
String name = en.nextString();
AlertRule rule = alertRuleTable.get(name);
/*
* if the Rule file is not existed, then also clear the conf-info
*/
File ruleFile = new File(root, name + ".alert");
if (ruleFile.canRead() == false || rule == null) {
clear(name);
continue;
}
if (ruleFile.lastModified() != rule.lastModified) {
rule = createRule(name, ruleFile);
alertRuleTable.put(name, rule);
}
File ruleConf = new File(root, name + ".conf");
AlertConf conf = alertConfTable.get(name);
if (conf.lastModified != ruleConf.lastModified()) {
conf = createConf(name, ruleConf);
alertConfTable.put(name, conf);
}
}
}
private String getRuleName(String name) {
name = name.substring(0, name.lastIndexOf('.'));
return name;
}
private File getConfFile(File ruleFile) {
if (ruleFile == null)
return null;
File conf = new File(ruleFile.getPath().substring(0, ruleFile.getPath().lastIndexOf('.')) + ".conf");
if (conf.canRead())
return conf;
else
return null;
}
// 각 룰에 대한 기본 설정을 로딩한다.
// 각 설정은 스크립트에서 변경할 수 있다.
private AlertConf createConf(String name, File confFile) {
AlertConf conf = new AlertConf();
if (confFile != null && confFile.canRead()) {
conf.lastModified = confFile.lastModified();
byte[] body = FileUtil.readAll(confFile);
if (body != null) {
Properties p = new Properties();
try {
p.load(new ByteArrayInputStream(body));
} catch (Exception e) {
}
conf.history_size = getInt(p, "history_size", 0);
conf.silent_time = getInt(p, "silent_time", 0);
conf.check_term = getInt(p, "check_term", 0);
}
}
return conf;
}
// 반복적인 컴파일 시도를 막기위해 한번 실패한 파일은 컴파일을 다시 시도하지 않도록 한다.
private LongSet compileErrorFiles = new LongSet();
private AlertRule createRule(String name, File ruleFile) {
long fileSignature = fileSign(ruleFile);
if (compileErrorFiles.contains(fileSignature))
return null;
try {
String body = new String(FileUtil.readAll(ruleFile));
ClassPool cp = ClassPool.getDefault();
// String jar = FileUtil.getJarFileName(AlertRule.class);
// if (jar != null) {
// cp.appendClassPath(jar);
// }
if(this.getClass().getClassLoader() instanceof URLClassLoader){
URLClassLoader u = (URLClassLoader)this.getClass().getClassLoader();
URL[] urls = u.getURLs();
for(int i = 0; urls!=null && i<urls.length ; i++){
//Logger.println("[Alert rule load classpath urls]" + urls[i].toString());
try {
cp.appendClassPath(urls[i].getFile());
} catch (NotFoundException e) {
Logger.println("S219", "[Error]" + e.getMessage());
}
}
}
name = "scouter.server.alert.impl." + name;
Class c = null;
CtClass cc = cp.get(AlertRule.class.getName());
CtClass impl = null;
CtMethod method = null;
try {
impl = cp.get(name);
impl.defrost();
method = impl.getMethod("process", "(" + nativeName(RealCounter.class) + ")V");
} catch (javassist.NotFoundException e) {
impl = cp.makeClass(name, cc);
method = CtNewMethod.make("public void process(" + RealCounter.class.getName() + " c){}", impl);
impl.addMethod(method);
}
method.setBody("{" + RealCounter.class.getName() + " $counter=$1;" + body + "\n}");
c = impl.toClass(new URLClassLoader(new URL[0], this.getClass().getClassLoader()), null);
AlertRule rule = (AlertRule) c.newInstance();
rule.lastModified = ruleFile.lastModified();
Logger.println("S215", "Detected new alert rule : " + ruleFile.getName());
return rule;
} catch (javassist.CannotCompileException ee) {
compileErrorFiles.add(fileSignature);
Logger.println("S212", ee.getMessage());
} catch (Exception e) {
Logger.println("S213", e);
}
return null;
}
private long fileSign(File f) {
if (f == null)
return 0;
String filename = f.getName();
long filetime = f.lastModified();
return BitUtil.setHigh(filetime, HashUtil.hash(filename));
}
private String nativeName(Class class1) {
return "L" + class1.getName().replace('.', '/') + ";";
}
protected int getInt(Properties p, String key, int defValue) {
String value = StringUtil.trimEmpty(p.getProperty(key));
if (value.length() == 0)
return defValue;
return CastUtil.cint(value);
}
}