package dk.kb.yggdrasil; import java.io.IOException; import java.util.concurrent.TimeUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.bridge.SLF4JBridgeHandler; import dk.kb.yggdrasil.bitmag.Bitrepository; import dk.kb.yggdrasil.bitmag.BitrepositoryConfig; import dk.kb.yggdrasil.config.Config; import dk.kb.yggdrasil.config.RabbitMqSettings; import dk.kb.yggdrasil.config.RunningMode; import dk.kb.yggdrasil.db.StateDatabase; import dk.kb.yggdrasil.exceptions.RabbitException; import dk.kb.yggdrasil.exceptions.YggdrasilException; import dk.kb.yggdrasil.messaging.MQ; import dk.kb.yggdrasil.messaging.RemotePreservationStateUpdater; import dk.kb.yggdrasil.utils.RunState; /** * Main class of the Yggdrasil Preservation service. * The running mode is defined by the java property {@link RunningMode#RUNNINGMODE_PROPERTY}. * * The configuration directory is defined by the property {@link #CONFIGURATION_DIRECTORY_PROPERTY} * The default value is ${user.home}/Yggdrasil/config * */ public class Main { /** Boolean for deciding whether to start main workflow (Normal mode) * or just shutdown program after initialization (Unittest mode). */ private static boolean isUnittestmode = false; /** Logging mechanism. */ private static Logger logger = LoggerFactory.getLogger(Main.class.getName()); /** The state database.*/ private StateDatabase sd; /** The messagequeue.*/ private MQ mq; /** The bitrepository interface.*/ private Bitrepository bitrepository; /** * Constructor. * @param sd The StateDatabase. * @param bitrepository The bitrepository interface. */ protected Main(StateDatabase sd, Bitrepository bitrepository) { this.sd = sd; this.bitrepository = bitrepository; } /** * Main program of the Yggdrasil preservation service. * @param args No args is read here. Properties are used to locate confdir and the running mode. * @throws YggdrasilException When unable to find a configuration directory or locate required settings files. */ public static void main(String[] args) throws YggdrasilException { SLF4JBridgeHandler.removeHandlersForRootLogger(); SLF4JBridgeHandler.install(); logger.info("Starting the Yggdrasil Main program"); logger.info("Initialising settings using runningmode '" + RunningMode.getMode() + "'"); if (args.length == 1 && args[0].equals("test")) { isUnittestmode = true; } Config config = new Config(); Bitrepository bitrepository = new Bitrepository(new BitrepositoryConfig(config.getBitmagConfigFile())); // Initiate call of StateDatabase StateDatabase sd = new StateDatabase(config.getYggdrasilConfig().getDatabaseDir()); HttpCommunication httpCommunication = new HttpCommunication(config.getYggdrasilConfig().getTemporaryDir()); RunState runnableRunState = new RunState(); Thread runstate = new Thread(runnableRunState); runstate.start(); Main main = new Main(sd, bitrepository); if (!isUnittestmode) { main.runWorkflow(config, httpCommunication); } else { logger.info("isUnittestmode = " + isUnittestmode); } logger.info("Shutting down the Yggdrasil Main program"); runstate.interrupt(); main.cleanup(); } /** * Cleanup. Closes Bitrepository and MQ. */ protected void cleanup() { bitrepository.shutdown(); try { if (!isUnittestmode) {this.mq.close();} } catch (IOException e) { logger.debug("Ignoring exception while closing down RabbitMQ", e); } sd.cleanup(); } /** * Initialize message queue (RabbitMQ). * @throws YggdrasilException When unable to connect to message queue. */ protected void initializeRabbitMQ(final RabbitMqSettings rabbitMqSettings) throws YggdrasilException { logger.info("Initialising RabbitMQ"); this.mq = null; try { this.mq = new MQ(rabbitMqSettings); this.mq.purgeQueue(rabbitMqSettings.getShutdownDestination()); } catch (RabbitException e) { String errMsg = "initializeRabbitMQ exception "; logger.error(errMsg, e); try { TimeUnit.MINUTES.sleep(rabbitMqSettings.getPollingIntervalInMinutes()); } catch (InterruptedException e1) { errMsg = "Slowing down workfow exception "; throw new YggdrasilException(errMsg, e1); } initializeRabbitMQ(rabbitMqSettings); } } /** * Run Yggdrasil workflow with slow down if needed. * @throws YggdrasilException When unable to connect to message queue. */ protected void runWorkflow(final Config config, final HttpCommunication httpCommunication) throws YggdrasilException { logger.info("Starting main workflow of Yggdrasil program"); this.initializeRabbitMQ(config.getMqSettings()); final Workflow wf = new Workflow(this.mq, sd, bitrepository, config.getYggdrasilConfig(), config.getModels(), httpCommunication, new RemotePreservationStateUpdater(mq)); logger.info("Ready to run workflow"); // Consider refactoring this at a time where the used rabbitmq.client.ConnectionFactory supports // the setAutomaticRecoveryEnabled and setNetworkRecoveryInterval methods. try { wf.run(); } catch (RabbitException e) { String errMsg = "runWorkflow exception "; logger.error(errMsg, e); try { TimeUnit.MINUTES.sleep(config.getMqSettings().getPollingIntervalInMinutes()); } catch (InterruptedException e1) { errMsg = "Slowing down workfow exception "; throw new YggdrasilException(errMsg, e1); } runWorkflow(config, httpCommunication); } } }