blob: 7a7d5ff228a16cf1ecc807400ff09745f992dd82 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 com.epam.dlab;
import com.epam.dlab.configuration.BillingToolConfiguration;
import com.epam.dlab.configuration.BillingToolConfigurationFactory;
import com.epam.dlab.configuration.SchedulerConfiguration;
import com.epam.dlab.core.parser.ParserBase;
import com.epam.dlab.exceptions.AdapterException;
import com.epam.dlab.exceptions.DlabException;
import com.epam.dlab.exceptions.InitializationException;
import com.epam.dlab.exceptions.ParseException;
import com.epam.dlab.util.ServiceUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Arrays;
/**
* Billing scheduler for loading billing report.
*/
public class BillingScheduler implements Runnable {
private static final Logger LOGGER = LoggerFactory.getLogger(BillingScheduler.class);
/**
* Timeout for check the schedule in milliseconds.
*/
private static final long CHECK_TIMEOUT_MILLIS = 60000;
/**
* Billing scheduler instance.
*/
private static BillingScheduler scheduler;
private final boolean enabled;
private final BillingToolConfiguration configuration;
/**
* Starts the scheduler for given configuration.
*
* @param filename the name of file for billing configuration.
* @throws InitializationException
*/
public static void start(String filename) throws InitializationException {
if (scheduler == null) {
scheduler = new BillingScheduler(filename);
scheduler.thread.start();
} else {
LOGGER.debug("Billing scheduler already started");
}
}
/**
* Stops the scheduler.
*/
public static void stop() {
if (scheduler.thread != null) {
LOGGER.debug("Billing scheduler will be stopped ...");
synchronized (scheduler.thread) {
scheduler.thread.interrupt();
scheduler.thread = null;
}
LOGGER.info("Scheduler has been stopped");
}
}
/**
* Thread of the scheduler.
*/
private Thread thread = new Thread(this, this.getClass().getSimpleName());
/**
* Name of configuration file.
*/
private final String confFilename;
/**
* Current schedule.
*/
private SchedulerConfiguration schedule;
/**
* Instantiate billing scheduler for given configuration.
*
* @param filename the name of file for billing configuration.
* @throws InitializationException
*/
public BillingScheduler(String filename) throws InitializationException {
this.confFilename = filename;
LOGGER.debug("Billing report configuration file: {}", filename);
configuration = BillingToolConfigurationFactory.build(confFilename, BillingToolConfiguration.class);
this.enabled = configuration.isBillingEnabled();
setSchedule(configuration);
}
/**
* Loads the billing report.
*
* @throws InitializationException
* @throws AdapterException
* @throws ParseException
*/
private void load() throws InitializationException, AdapterException, ParseException {
ParserBase parser = configuration.build();
long time = schedule.getNearTime().getTimeInMillis();
if (setSchedule(configuration)) {
if (time != schedule.getNearTime().getTimeInMillis()) {
LOGGER.info("Previous billing schedule has been canceled");
return;
}
}
LOGGER.info("Try to laod billing report for configuration: {}", configuration);
parser.parse();
if (!parser.getStatistics().isEmpty()) {
LOGGER.info("Billing report parser statistics:");
for (int i = 0; i < parser.getStatistics().size(); i++) {
LOGGER.info(" {}", parser.getStatistics().get(i).toString());
}
}
}
/**
* Read the schedule from configuration.
*
* @param configuration the billing configuration.
* @return <b>true>/b> if new schedule was loaded, otherwise <b>false</b>.
* @throws InitializationException
*/
private boolean setSchedule(BillingToolConfiguration configuration) throws InitializationException {
SchedulerConfiguration schedulerConfiguration = configuration.getScheduler();
boolean isModified = false;
if (schedulerConfiguration == null) {
throw new InitializationException(String.format("Schedule of billing report in configuration file \"%s " +
"not found", confFilename));
}
if (this.schedule == null) {
isModified = true;
LOGGER.debug("Billing report schedule: {}", schedulerConfiguration);
} else {
this.schedule.adjustStartTime();
if (!schedulerConfiguration.equals(this.schedule)) {
isModified = true;
LOGGER.debug("New billing report schedule has been loaded: {}", schedulerConfiguration);
}
}
try {
this.schedule = new SchedulerConfiguration();
this.schedule.setSchedule(schedulerConfiguration.getSchedule());
this.schedule.build();
} catch (Exception e) {
throw new InitializationException("Cannot configure billing scheduler. " + e.getLocalizedMessage(), e);
}
return isModified;
}
@Override
public void run() {
if (enabled) {
LOGGER.info("Billing scheduler has been started");
long startTimeMillis = schedule.getNextTime().getTimeInMillis();
long timeMillis;
LOGGER.info("Billing report will be loaded at {}", schedule.getNextTime().getTime());
try {
while (!Thread.currentThread().isInterrupted()) {
if (startTimeMillis <= System.currentTimeMillis()) {
try {
LOGGER.debug("Try to load billing report for schedule {}",
schedule.getNextTime().getTime());
load();
} catch (InitializationException | AdapterException | ParseException e) {
LOGGER.error("Error loading billing report: {}", e.getLocalizedMessage(), e);
}
startTimeMillis = schedule.getNextTime().getTimeInMillis();
LOGGER.info("Billing report will be loaded at {}", schedule.getNextTime().getTime());
} else {
schedule.adjustStartTime();
timeMillis = schedule.getNextTime().getTimeInMillis();
if (startTimeMillis != timeMillis) {
LOGGER.info("Billing report will be loaded at {}", schedule.getNextTime().getTime());
startTimeMillis = timeMillis;
}
}
try {
timeMillis = startTimeMillis - System.currentTimeMillis();
if (timeMillis > 0) {
timeMillis = Math.min(CHECK_TIMEOUT_MILLIS, timeMillis);
Thread.sleep(timeMillis);
}
} catch (InterruptedException e) {
LOGGER.warn("Billing scheduler interrupted", e);
Thread.currentThread().interrupt();
}
}
} catch (Exception e) {
LOGGER.error("Unhandled billing report error: {}", e.getLocalizedMessage(), e);
}
LOGGER.info("Scheduler has been stopped");
} else {
LOGGER.info("Billing scheduler is disabled");
}
}
/**
* Runs billing scheduler for given configuration file.
*
* @param args the arguments of command line.
* @throws InitializationException
*/
public static void startScheduler(String[] args) throws InitializationException {
if (ServiceUtils.printAppVersion(BillingTool.class, args)) {
return;
}
String confName = null;
for (int i = 0; i < args.length; i++) {
if (BillingTool.isKey("help", args[i])) {
i++;
Help.usage(i < args.length ? Arrays.copyOfRange(args, i, args.length) : null);
return;
} else if (BillingTool.isKey("conf", args[i])) {
i++;
if (i < args.length) {
confName = args[i];
} else {
throw new InitializationException("Missing the name of configuration file");
}
}
}
if (confName == null) {
Help.usage();
throw new InitializationException("Missing arguments");
}
BillingTool.setLoggerLevel();
try {
start(confName);
} catch (Exception e) {
throw new DlabException("Billing scheduler failed", e);
}
}
}