Java tutorial
/* # # Copyright (C) 2009-2011 Anders Hl, Ingenjorsbyn AB # # 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, see <http://www.gnu.org/licenses/>. # */ package com.ingby.socbox.bischeck; import static org.quartz.JobBuilder.newJob; import static org.quartz.impl.matchers.EverythingMatcher.allJobs; import java.io.BufferedReader; import java.io.DataInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLineParser; import org.apache.commons.cli.GnuParser; import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.Options; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.quartz.JobDataMap; import org.quartz.JobDetail; import org.quartz.JobListener; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.Trigger; import org.quartz.TriggerKey; import org.quartz.impl.StdSchedulerFactory; import org.quartz.impl.matchers.GroupMatcher; import com.ingby.socbox.bischeck.cache.CacheException; import com.ingby.socbox.bischeck.cache.CacheFactory; import com.ingby.socbox.bischeck.configuration.ConfigFileManager; import com.ingby.socbox.bischeck.configuration.ConfigurationJobs; import com.ingby.socbox.bischeck.configuration.ConfigurationManager; import com.ingby.socbox.bischeck.internal.InternalSurveillance; import com.ingby.socbox.bischeck.servers.ServerMessageExecutor; import com.ingby.socbox.bischeck.service.ExecuteServiceOnDemand; import com.ingby.socbox.bischeck.service.ServiceJob; import com.ingby.socbox.bischeck.service.ServiceJobConfig; /** * The Execute class is the main class to start Bischeck. * */ public final class Execute implements ExecuteMBean { private static final Logger LOGGER = LoggerFactory.getLogger(Execute.class); private static Object syncObj = new Object(); private Boolean shutdownRequested = false; private Boolean reloadRequested = false; private Integer reloadcount = 0; private Long reloadtime = 0L; private Boolean allowReload = false; private static Execute exec = new Execute(); private MBeanManager mbsMgr = null; private Scheduler sched = null; private static final int RESTART = 1000; private static final String XML_CONFIG_DIRECTORY = "xmlconfigdir"; private static final String BIS_HOME_DIRECTORY = "bishome"; private static final long LOOPTIMEOUTDEF = 30000; private static final long SHUTDOWNSLEEPDEF = 3000; private static long looptimeout = LOOPTIMEOUTDEF; private static long shutdownsleep = SHUTDOWNSLEEPDEF; private static String bischeckversion; private static Thread dumpthread; private static ExecuteServiceOnDemand exon = new ExecuteServiceOnDemand(); public static void main(String[] args) { // create the command line parser CommandLineParser parser = new GnuParser(); CommandLine line = null; // create the Options Options options = new Options(); options.addOption("u", "usage", false, "show usage."); options.addOption("d", "deamon", false, "start as a deamon"); try { // parse the command line arguments line = parser.parse(options, args); } catch (org.apache.commons.cli.ParseException e) { System.out.println("Command parse error:" + e.getMessage()); HelpFormatter formatter = new HelpFormatter(); formatter.printHelp("Bischeck", options); Util.ShellExit(Util.FAILED); } if (line.hasOption("usage")) { HelpFormatter formatter = new HelpFormatter(); formatter.printHelp("Bischeck", options); Util.ShellExit(Util.OKAY); } dumpthread = new Thread() { public void run() { try { CacheFactory.destroy(); } catch (CacheException e) { LOGGER.warn("Cache could not be destoryed", e); } } }; dumpthread.setName("dumpcache"); int retStat = Util.OKAY; do { try { if (line.hasOption("deamon")) { ConfigurationManager.init(); } else { ConfigurationManager.initonce(); } } catch (Exception e) { LOGGER.error("Creating bischeck Configuration Manager failed with: {}", e.getMessage(), e); Util.ShellExit(Util.FAILED); } retStat = Execute.getInstance().deamon(); LOGGER.debug("Method Execute returned {}", retStat); } while (retStat == RESTART); dumpthread.start(); LOGGER.info("******************* Shutdown ********************"); Util.ShellExit(retStat); } private Execute() { mbsMgr = new MBeanManager(this, ExecuteMBean.BEANNAME); mbsMgr.registerMBeanserver(); } private static Execute getInstance() { return exec; } private int deamon() { LOGGER.info("******************** Startup *******************"); /* * Stuff that should only be done on the first start and never on reload */ if (!reloadRequested) { try { InternalSurveillance.init(); deamonInit(); } catch (Exception e) { LOGGER.error("Deamon init failed - exit", e); return Util.FAILED; } try { CacheFactory.init(); } catch (CacheException ce) { LOGGER.error("Cache factory init failed - exit", ce); return Util.FAILED; } } /* * Reset the shutdown and reload flags */ shutdownRequested = false; reloadRequested = false; if (!start()) { return Util.FAILED; } /* * Enter loop if deamonMode */ deamonLoop(); stop(); ServerMessageExecutor.getInstance().unregisterAll(); if (reloadRequested) { return RESTART; } else { return Util.OKAY; } } /** * The first time the daemon method is called this method will be invoked to * setup specific task to become a daemon process. This include: Checking * pid file so no other bischeck daemon exists. Setup pid file delete on * exit. Close all standard file - in, out and error. Add shutdown hooks for * OS signals to get controlled process exit. * * @throws Exception * disable off SSL X.509 validation failed */ private void deamonInit() throws Exception { Boolean disableCertificateValidation = Boolean.valueOf(ConfigurationManager.getInstance().getProperties() .getProperty("disableCertificateValidation", "false")); if (disableCertificateValidation) { try { SSLTrustManager.disableCertificateValidation(); } catch (KeyManagementException e) { LOGGER.error("Disable SSL X.509 certification validation failed", e); throw new Exception(e); } catch (NoSuchAlgorithmException e) { LOGGER.error("Disable SSL X.509 certification validation failed", e); throw new Exception(e); } } ConfigurationManager.getInstance().getPidFile().deleteOnExit(); setupProperties(); try { System.in.close(); } catch (IOException ignore) { LOGGER.info("Could not close stdin"); } System.out.close(); System.err.close(); bischeckversion = readBischeckVersion(); addDeamonShutdownHook(); } private void setupProperties() { try { looptimeout = Long.parseLong(ConfigurationManager.getInstance().getProperties() .getProperty("loopTimeout", "" + LOOPTIMEOUTDEF)); } catch (NumberFormatException ne) { LOGGER.warn("Property loopTimeout was not a number. Set to {}", LOOPTIMEOUTDEF, ne); looptimeout = LOOPTIMEOUTDEF; } try { shutdownsleep = Long.parseLong(ConfigurationManager.getInstance().getProperties() .getProperty("shutdownWait", "" + SHUTDOWNSLEEPDEF)); } catch (NumberFormatException ne) { LOGGER.warn("Property shutdownWait no correctly set. Set to {}", SHUTDOWNSLEEPDEF, ne); shutdownsleep = SHUTDOWNSLEEPDEF; } } /** * The loop to enter until shutdown or reload signal. If in DEBUG log level * each quartz trigger scheduled is printed every LOOPTIMEOUT ms. */ private void deamonLoop() { allowReload = true; do { try { synchronized (syncObj) { syncObj.wait(looptimeout); } } catch (InterruptedException ignore) { LOGGER.info("Interrupted while loop timeout", ignore); } // If no remaining triggers - shutdown if (getNumberOfTriggers() == 0) { LOGGER.debug("Number of triggers zero"); shutdown(); } if (LOGGER.isDebugEnabled()) { // Show next fire time for all triggers String[] list = getTriggers(); LOGGER.debug("****** Next fire time *********"); for (int i = 0; i < list.length; i++) { LOGGER.debug(list[i]); } LOGGER.debug("*******************************"); } } while (!isShutdownRequested()); allowReload = false; } /** * Setup all the quartz job that is configured. * * @param sched * - the quartz scheduler * @throws SchedulerException */ private void initTriggers(Scheduler sched) throws SchedulerException { List<ServiceJobConfig> schedulejobs = ConfigurationManager.getInstance().getScheduleJobConfigs(); for (ServiceJobConfig jobentry : schedulejobs) { LOGGER.info("Configure service job - {}", jobentry.getService().getServiceName()); Map<String, Object> map = new HashMap<String, Object>(); map.put("service", jobentry.getService()); createJob(sched, jobentry, map); } } private void createJob(Scheduler sched, ServiceJobConfig jobentry, Map<String, Object> map) throws SchedulerException { JobDataMap jobmap = new JobDataMap(map); int jobid = 0; for (Trigger trigger : jobentry.getSchedules()) { if (trigger != null) { try { JobDetail job = newJob(ServiceJob.class) .withIdentity(jobentry.getService().getServiceName() + (jobid++), jobentry.getService().getHost().getHostname()) .withDescription(jobentry.getService().getHost().getHostname() + "-" + jobentry.getService().getServiceName()) .usingJobData(jobmap).build(); sched.scheduleJob(job, trigger); LOGGER.info("Adding trigger to job {}", trigger.toString()); } catch (SchedulerException e) { LOGGER.warn("Scheduled job failed with exception {}", e.getMessage(), e); throw e; } } } } /** * Create and initialize the quartz scheduler to use. * * @param sched * @return the scheduler created * @throws SchedulerException * if the scheduler can not be created or it can not be started */ private Scheduler initScheduler() throws SchedulerException { Scheduler sched = null; try { LOGGER.info("Start scheduler"); sched = StdSchedulerFactory.getDefaultScheduler(); sched.start(); } catch (SchedulerException e) { LOGGER.warn("Scheduler failed to start with exception - {}", e.getMessage(), e); throw e; } JobListener jobListener = new JobListenerLogger(); try { LOGGER.info("Add scheduler listener"); sched.getListenerManager().addJobListener(jobListener, allJobs()); } catch (SchedulerException e) { LOGGER.warn("Add listener failed with exception {}", e.getMessage(), e); throw e; } return sched; } /** * Check if the a shutdown has been requested by any thread. * * @return */ private boolean isShutdownRequested() { return shutdownRequested; } /** * Setup a OS hook to catch a shutdown signal. */ protected void addDeamonShutdownHook() { Runtime.getRuntime().addShutdownHook(new Thread() { public void run() { shutdown(); try { dumpthread.join(); } catch (InterruptedException ignore) { LOGGER.info("Interrupted while waiting on dumpthread thread to complete", ignore); } } }); } /* * * JMX methods */ @Override public boolean start() { try { sched = initScheduler(); initTriggers(sched); ConfigurationJobs.initAdminJobs(); } catch (SchedulerException e) { LOGGER.error("Scheduler init failed", e); return false; } return true; } @Override public boolean stop() { try { sched.shutdown(true); LOGGER.info("Scheduler shutdown"); } catch (SchedulerException e) { LOGGER.warn("Stopping Quartz scheduler failed", e); return false; } return true; } @Override public void shutdown() { LOGGER.info("Shutdown request"); shutdownRequested = true; synchronized (syncObj) { syncObj.notify(); } try { Thread.sleep(shutdownsleep); } catch (InterruptedException ignore) { LOGGER.info("Interrupted while waiting on main deamon thread to complete", ignore); } } @Override public boolean reload() { if (allowReload) { LOGGER.info("Reload request"); reloadcount++; reloadtime = System.currentTimeMillis(); reloadRequested = true; shutdown(); return true; } else { LOGGER.warn("Not allowed to reload"); return false; } } @Override public long getReloadTime() { return reloadtime; } @Override public int getReloadCount() { return reloadcount; } @Override public String getBischeckHome() { return System.getProperty(BIS_HOME_DIRECTORY); } @Override public String getXmlConfigDir() { if (System.getProperty(XML_CONFIG_DIRECTORY) == null) { return ConfigFileManager.DEFAULT_CONFIGDIR; } else { return System.getProperty(XML_CONFIG_DIRECTORY); } } @Override public String getBischeckVersion() { return bischeckversion; } @Override public int getCacheClassHit() { return ClassCache.cacheHit(); } @Override public int getCacheClassMiss() { return ClassCache.cacheMiss(); } @Override public String[] getTriggers() { List<String> triggerList = new ArrayList<String>(); try { Scheduler sched = StdSchedulerFactory.getDefaultScheduler(); List<String> triggerGroups = sched.getTriggerGroupNames(); for (String triggergroup : triggerGroups) { Set<TriggerKey> keys = sched.getTriggerKeys(GroupMatcher.triggerGroupEquals(triggergroup)); Iterator<TriggerKey> iter = keys.iterator(); while (iter.hasNext()) { TriggerKey tiggerkey = iter.next(); Trigger trigger = sched.getTrigger(tiggerkey); triggerList.add(sched.getJobDetail(trigger.getJobKey()).getDescription() + " next fire time " + trigger.getNextFireTime()); } } } catch (SchedulerException se) { LOGGER.error("Build trigger list failed with exception - {}", se.getMessage(), se); } String[] arr = new String[triggerList.size()]; triggerList.toArray(arr); return arr; } /** * Count the number of active quartz jobs running. The total count is * subtracted with the number of admin jobs started by * {@link ConfigurationManager}. * * @return number of service jobs */ public int getNumberOfTriggers() { int numberoftriggers = 0; try { Scheduler sched = StdSchedulerFactory.getDefaultScheduler(); List<String> triggerGroups = sched.getTriggerGroupNames(); for (String triggergroup : triggerGroups) { Set<TriggerKey> keys = sched.getTriggerKeys(GroupMatcher.triggerGroupEquals(triggergroup)); numberoftriggers += keys.size(); } } catch (SchedulerException se) { LOGGER.error("Build trigger list failed with exception - {}", se.getMessage(), se); } return numberoftriggers - ConfigurationJobs.numberOfAdminJobs(); } private String readBischeckVersion() { String version; String path = null; if (System.getProperty(BIS_HOME_DIRECTORY) != null) { path = System.getProperty(BIS_HOME_DIRECTORY); } else { LOGGER.error("System property bishome must be set"); } try (FileInputStream fstream = new FileInputStream(path + File.separator + "version.txt"); DataInputStream in = new DataInputStream(fstream); BufferedReader br = new BufferedReader(new InputStreamReader(in))) { version = br.readLine(); LOGGER.info("Bisheck version is {}", version); } catch (Exception ioe) { version = "N/A"; LOGGER.warn("Can not determine the bischeck version", ioe); } return version; } }