Java tutorial
/* Smart Cloud/City Engine backend (SCE). Copyright (C) 2015 DISIT Lab http://www.disit.org - University of Florence This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package sce; import java.net.*; import java.io.*; import java.util.ArrayList; import java.util.Date; import java.util.Map; import org.quartz.InterruptableJob; import org.quartz.UnableToInterruptJobException; import org.quartz.JobDataMap; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.quartz.JobKey; import org.quartz.SchedulerException; import org.quartz.SchedulerMetaData; import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import org.json.simple.parser.ParseException; import java.util.Enumeration; import java.util.Iterator; import java.util.Scanner; /*@DisallowConcurrentExecution * An annotation that marks a Job * class as one that must not have * multiple instances executed concurrently * (where instance is based-upon a JobDetail * definition - or in other words based upon a JobKey). */ /*@PersistJobDataAfterExecution * An annotation that marks a Job class as one that makes * updates to its JobDataMap during execution, and wishes the * scheduler to re-store the JobDataMap when execution completes. * Jobs that are marked with this annotation should also seriously * consider using the DisallowConcurrentExecution annotation, to avoid * data storage race conditions with concurrently executing job instances. * If you use the @PersistJobDataAfterExecution annotation, you should strongly * consider also using the @DisallowConcurrentExecution annotation, in order to * avoid possible confusion (race conditions) of what data was left stored when two * instances of the same job (JobDetail) executed concurrently. */ public class ElasticJob implements InterruptableJob { private URLConnection urlConnection; public ElasticJob() { } @Override public void execute(JobExecutionContext context) throws JobExecutionException { try { // build the list of queries ArrayList<String> queries = new ArrayList<>(); JobDataMap jobDataMap = context.getJobDetail().getJobDataMap(); String url = jobDataMap.getString("#url"); String elasticJob = jobDataMap.getString("#elasticJobConstraints"); int counter = 0; String expression = ""; String j_tmp = ""; JSONParser parser = new JSONParser(); JSONObject jsonobject = (JSONObject) parser.parse(elasticJob); Iterator<?> keys = jsonobject.keySet().iterator(); while (keys.hasNext()) { String i = (String) keys.next(); JSONObject jsonobject2 = (JSONObject) jsonobject.get(i); Iterator<?> keys2 = jsonobject2.keySet().iterator(); while (keys2.hasNext()) { String j = (String) keys2.next(); JSONObject jsonobject3 = (JSONObject) jsonobject2.get(j); String configuration = ""; if (jsonobject3.get("slaconfiguration") != null) { configuration = (String) jsonobject3.get("slaconfiguration"); } else if (jsonobject3.get("bcconfiguration") != null) { configuration = (String) jsonobject3.get("bcconfiguration"); } else if (jsonobject3.get("vmconfiguration") != null) { configuration = (String) jsonobject3.get("vmconfiguration"); } else if (jsonobject3.get("anyconfiguration") != null) { configuration = (String) jsonobject3.get("anyconfiguration"); } // add the query to the queries list queries.add(getQuery((String) jsonobject3.get("metric"), (String) jsonobject3.get("cfg"), configuration, (String) jsonobject3.get("relation"), String.valueOf(jsonobject3.get("threshold")), String.valueOf(jsonobject3.get("time")), (String) jsonobject3.get("timeselect"))); String op = jsonobject3.get("match") != null ? (String) jsonobject3.get("match") : ""; switch (op) { case "": break; case "any": op = "OR("; break; case "all": op = "AND("; break; } String closed_parenthesis = " "; int num_closed_parenthesis = !j_tmp.equals("") ? Integer.parseInt(j_tmp) - Integer.parseInt(j) : 0; for (int parenthesis = 0; parenthesis < num_closed_parenthesis; parenthesis++) { closed_parenthesis += " )"; } expression += op + " " + counter + closed_parenthesis; j_tmp = j; counter++; } } ExpressionTree calc = new ExpressionTree(new Scanner(expression), queries); if (calc.evaluate()) { URL u = new URL(url); //get user credentials from URL, if present final String usernamePassword = u.getUserInfo(); //set the basic authentication credentials for the connection if (usernamePassword != null) { Authenticator.setDefault(new Authenticator() { @Override protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication(usernamePassword.split(":")[0], usernamePassword.split(":")[1].toCharArray()); } }); } //call the callUrl URLConnection connection = u.openConnection(); getUrlContents(connection); } } catch (Exception e) { e.printStackTrace(); throw new JobExecutionException(e); } } @Override public void interrupt() throws UnableToInterruptJobException { ((HttpURLConnection) this.urlConnection).disconnect(); } public String readURL(String url) throws JobExecutionException { try { String result = ""; URL oracle = new URL(url); try (BufferedReader in = new BufferedReader(new InputStreamReader(oracle.openStream()))) { String inputLine; while ((inputLine = in.readLine()) != null) { result += inputLine; } } return result; } catch (IOException e) { throw new JobExecutionException(e); } } private String getUrlContents(URLConnection urlConnection) throws JobExecutionException { StringBuilder content = new StringBuilder(); try { try (BufferedReader bufferedReader = new BufferedReader( new InputStreamReader(urlConnection.getInputStream()))) { String line; while ((line = bufferedReader.readLine()) != null) { content.append(line); } } } catch (IOException e) { throw new JobExecutionException(e); } return content.toString(); } //send a notification email upon job completion public void sendEmail(JobExecutionContext context, String email) throws JobExecutionException { try { Date d = new Date(); String message = "Job was executed at: " + d.toString() + "\n"; SchedulerMetaData schedulerMetadata = context.getScheduler().getMetaData(); //Get the scheduler instance id message += "Scheduler Instance Id: " + schedulerMetadata.getSchedulerInstanceId() + "\n"; //Get the scheduler name message += "Scheduler Name: " + schedulerMetadata.getSchedulerName() + "\n"; try { //Get the scheduler ip Enumeration<NetworkInterface> n = NetworkInterface.getNetworkInterfaces(); message += "Scheduler IP: "; for (; n.hasMoreElements();) { NetworkInterface e = n.nextElement(); //System.out.println("Interface: " + e.getName()); Enumeration<InetAddress> a = e.getInetAddresses(); for (; a.hasMoreElements();) { InetAddress addr = a.nextElement(); message += !addr.getHostAddress().equals("127.0.0.1") ? addr.getHostAddress() + " " : ""; } } message += "\n"; } catch (SocketException e) { throw new JobExecutionException(e); } //Returns the result (if any) that the Job set before its execution completed (the type of object set as the result is entirely up to the particular job). //The result itself is meaningless to Quartz, but may be informative to JobListeners or TriggerListeners that are watching the job's execution. message += "Result: " + (context.getResult() != null ? truncateResult((String) context.getResult()) : "") + "\n"; //Get the unique Id that identifies this particular firing instance of the trigger that triggered this job execution. It is unique to this JobExecutionContext instance as well. message += "Fire Instance Id: " + (context.getFireInstanceId() != null ? context.getFireInstanceId() : "") + "\n"; //Get a handle to the Calendar referenced by the Trigger instance that fired the Job. message += "Calendar: " + (context.getCalendar() != null ? context.getCalendar().getDescription() : "") + "\n"; //The actual time the trigger fired. For instance the scheduled time may have been 10:00:00 but the actual fire time may have been 10:00:03 if the scheduler was too busy. message += "Fire Time: " + (context.getFireTime() != null ? context.getFireTime() : "") + "\n"; //the job name message += "Job Name: " + (context.getJobDetail().getKey() != null ? context.getJobDetail().getKey().getName() : "") + "\n"; //the job group message += "Job Group: " + (context.getJobDetail().getKey() != null ? context.getJobDetail().getKey().getGroup() : "") + "\n"; //The amount of time the job ran for (in milliseconds). The returned value will be -1 until the job has actually completed (or thrown an exception), and is therefore generally only useful to JobListeners and TriggerListeners. //message += "RunTime: " + context.getJobRunTime() + "\n"; //the next fire time message += "Next Fire Time: " + (context.getNextFireTime() != null && context.getNextFireTime().toString() != null ? context.getNextFireTime().toString() : "") + "\n"; //the previous fire time message += "Previous Fire Time: " + (context.getPreviousFireTime() != null && context.getPreviousFireTime().toString() != null ? context.getPreviousFireTime().toString() : "") + "\n"; //refire count message += "Refire Count: " + context.getRefireCount() + "\n"; //job data map message += "\nJob data map: \n"; JobDataMap jobDataMap = context.getJobDetail().getJobDataMap(); for (Map.Entry<String, Object> entry : jobDataMap.entrySet()) { message += entry.getKey() + " = " + entry.getValue() + "\n"; } Mail.sendMail("SCE notification", message, email, null, null); } catch (SchedulerException e) { throw new JobExecutionException(e); } } //trigger the linked jobs of the finished job, depending on the job result [true, false] public void jobChain(JobExecutionContext context) throws JobExecutionException { try { //get the job data map JobDataMap jobDataMap = context.getJobDetail().getJobDataMap(); //get the finished job result (true/false) String resultjob = context.getResult() != null ? (String) context.getResult() : null; if (jobDataMap.containsKey("#nextJobs") && resultjob != null) { //read json from request JSONParser parser = new JSONParser(); Object obj = parser.parse(jobDataMap.getString("#nextJobs")); JSONArray jsonArray = (JSONArray) obj; for (int i = 0; i < jsonArray.size(); i++) { JSONObject jsonobject = (JSONObject) jsonArray.get(i); String operator = (String) jsonobject.get("operator"); String res = (String) jsonobject.get("result"); String nextJobName = (String) jsonobject.get("jobName"); String nextJobGroup = (String) jsonobject.get("jobGroup"); //if condition is satisfied, trigger the new job it does exist in the scheduler if ((operator.equals("==") && !isNumeric(resultjob) && !isNumeric(res) && res.equalsIgnoreCase(resultjob)) || (operator.equals("==") && isNumeric(resultjob) && isNumeric(res) && Double.parseDouble(resultjob) == Double.parseDouble(res)) || (operator.equals("!=") && !isNumeric(resultjob) && !isNumeric(res) && !res.equalsIgnoreCase(resultjob)) || (operator.equals("!=") && isNumeric(resultjob) && isNumeric(res) && Double.parseDouble(resultjob) != Double.parseDouble(res)) || (operator.equals("<") && isNumeric(resultjob) && isNumeric(res) && Double.parseDouble(resultjob) < Double.parseDouble(res)) || (operator.equals(">") && isNumeric(resultjob) && isNumeric(res) && Double.parseDouble(resultjob) > Double.parseDouble(res)) || (operator.equals("<=") && isNumeric(resultjob) && isNumeric(res) && Double.parseDouble(resultjob) <= Double.parseDouble(res)) || (operator.equals(">=") && isNumeric(resultjob) && isNumeric(res) && Double.parseDouble(resultjob) >= Double.parseDouble(res))) { //if nextJobName contains email(s), then send email(s) with obtained result, instead of triggering jobs if (nextJobName.contains("@") && nextJobGroup.equals(" ")) { sendEmail(context, nextJobName); } //else trigger next job else if (context.getScheduler().checkExists(JobKey.jobKey(nextJobName, nextJobGroup))) { context.getScheduler().triggerJob(JobKey.jobKey(nextJobName, nextJobGroup)); } } } } } catch (SchedulerException | ParseException e) { throw new JobExecutionException(e); } } //test if a string is numeric public boolean isNumeric(String str) { try { Double.parseDouble(str); } catch (NumberFormatException e) { return false; } return true; } //return a truncated result if too long for displaying public String truncateResult(String result) { if (result != null) { return result.length() > 30 ? result.substring(0, 29) + "..." : result; } return result; } //not used public static URL convertToURLEscapingIllegalCharacters(String string) { try { String decodedURL = URLDecoder.decode(string, "UTF-8"); URL url = new URL(decodedURL); URI uri = new URI(url.getProtocol(), url.getUserInfo(), url.getHost(), url.getPort(), url.getPath(), url.getQuery(), url.getRef()); return uri.toURL(); } catch (MalformedURLException | URISyntaxException | UnsupportedEncodingException e) { e.printStackTrace(System.out); return null; } } // args[0] = metric // args[1] = cfg // args[2] = slaconfiguration, bcconfiguration, vmconfiguration, anyconfiguration public static String getQuery(String... args) { String metric_name = args[0]; String cfg = args[1]; //sla, bc, vm, any String configuration = args[2]; String relation = args[3]; String threshold = args[4]; String time = args[5]; String timeselect = args[6]; //s, min, h, day, month String query = ""; switch (relation) { case ("ABOVE"): relation = ">"; break; case ("BELOW"): relation = "<"; break; } switch (timeselect) { case ("s"): timeselect = "SECOND"; break; case ("min"): timeselect = "MINUTE"; break; case ("h"): timeselect = "HOUR"; break; case ("day"): timeselect = "DAY"; break; case ("week"): timeselect = "WEEK"; break; case ("month"): timeselect = "MONTH"; break; } switch (cfg) { case ("sla"): query = "SELECT IF(AVG(value) " + relation + " " + (1 + (Double.parseDouble(threshold) / 100)) + " * threshold, true, false) AS result FROM quartz.QRTZ_SPARQL WHERE sla = '" + configuration + "' AND metric_name = '" + metric_name + "' AND metric_timestamp >= NOW() - INTERVAL " + time + " " + timeselect; break; case ("bc"): query = "SELECT IF(AVG(value) " + relation + " " + (1 + (Double.parseDouble(threshold) / 100)) + " * threshold, true, false) AS result FROM quartz.QRTZ_SPARQL WHERE business_configuration = '" + configuration + "' AND metric_name = '" + metric_name + "' AND metric_timestamp >= NOW() - INTERVAL " + time + " " + timeselect; break; case ("vm"): query = "SELECT IF(AVG(value) " + relation + " " + (1 + (Double.parseDouble(threshold) / 100)) + " * threshold, true, false) AS result FROM quartz.QRTZ_SPARQL WHERE virtual_machine_name = '" + configuration + "' AND metric_name = '" + metric_name + "' AND metric_timestamp >= NOW() - INTERVAL " + time + " " + timeselect; break; case ("any"): query = "SELECT IF(AVG(value) " + relation + " " + (1 + (Double.parseDouble(threshold) / 100)) + " * threshold, true, false) AS result FROM quartz.QRTZ_SPARQL WHERE metric_name = '" + metric_name + "' AND metric_timestamp >= NOW() - INTERVAL " + time + " " + timeselect; break; } return query; } }