*/
package fr.gouv.finances.dgfip.xemelios.batch;
-import fr.gouv.finances.cp.utils.PropertiesExpansion;
-import fr.gouv.finances.dgfip.utils.xml.FactoryProvider;
-import fr.gouv.finances.dgfip.xemelios.common.Constants;
-import fr.gouv.finances.dgfip.xemelios.common.config.DocumentsModel;
-import fr.gouv.finances.dgfip.xemelios.common.config.Loader;
-import fr.gouv.finances.dgfip.xemelios.data.DataConfigurationException;
-import fr.gouv.finances.dgfip.xemelios.data.DataLayerManager;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.util.StringTokenizer;
import java.util.Timer;
import java.util.TimerTask;
+
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;
+
import org.apache.log4j.Logger;
import org.apache.log4j.xml.DOMConfigurator;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
+import fr.gouv.finances.cp.utils.PropertiesExpansion;
+import fr.gouv.finances.dgfip.utils.xml.FactoryProvider;
+import fr.gouv.finances.dgfip.xemelios.common.Constants;
+import fr.gouv.finances.dgfip.xemelios.common.config.DocumentsModel;
+import fr.gouv.finances.dgfip.xemelios.common.config.Loader;
+import fr.gouv.finances.dgfip.xemelios.data.DataConfigurationException;
+import fr.gouv.finances.dgfip.xemelios.data.DataLayerManager;
+
/**
* This class plans batches, according to configuration file
+ *
* @author chm
*/
public class BatchRunner {
- private static final Logger logger = Logger.getLogger(BatchRunner.class);
- public static final String DATE_FORMAT="HH:mm";
- private static final SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT);
- private static final SimpleDateFormat debugSdf = new SimpleDateFormat("yyyy/MM/dd HH:mm");
-
- public static final int DEFAULT_STOP_PORT = 8006;
- public static final int DEFAULT_INFO_PORT = 8007;
- private File configFile;
- private ArrayList<MyTimer> timers;
-
- private static BatchRunner instance;
- private ArrayList<Batch> runningBatches;
- private BatchInformationListener infoListener = null;
- private boolean cancelled = false;
-
- private DocumentsModel documents;
-
- private PropertiesExpansion applicationConfiguration;
-
- /**
- * Point d'entrée
- * Cette classe est lancée avec l'environnement définit en propriétés système.
- * On les récupères pour faire un <code>applicationConfiguration</code> et le
- * transmettre au besoin.
- * @param args
- */
- public static void main(String args[]) {
- if (args.length == 0) {
- displaySyntax();
- throw new RuntimeException("invalid syntax");
- }
- File configFileName = new File(args[0]);
- if (!configFileName.exists()) {
- displaySyntax();
- throw new RuntimeException("config file not found: " + configFileName.getAbsolutePath());
- }
- if (args.length == 2 && "-stop".equals(args[1])) {
- new BatchRunner(configFileName, true);
- } else {
- new BatchRunner(configFileName);
- }
- }
-
- public BatchRunner(File configFile) {
- super();
- applicationConfiguration = restoreProperties();
- if(instance==null) {
- instance = this;
- runningBatches = new ArrayList<Batch>();
- }
- this.configFile = configFile;
- try {
- preInit();
- } catch(Throwable t) {
- t.printStackTrace();
- return;
- }
- try {
- initialize();
- } catch (Exception ex) {
- ex.printStackTrace();
- }
- }
-
- private static PropertiesExpansion restoreProperties() {
- PropertiesExpansion applicationConfiguration = new PropertiesExpansion(System.getProperties());
- // on va lire le mysql.properties
-// File f = new File(applicationConfiguration.getProperty(Constants.))
- return applicationConfiguration;
- }
-
- public BatchRunner(File configFile, boolean stop) {
- super();
- applicationConfiguration = restoreProperties();
- if(instance==null) {
- instance = this;
- runningBatches = new ArrayList<Batch>();
- }
- this.configFile = configFile;
- try {
- preInit();
- } catch(Throwable t) {
- return;
- }
- if (!stop) {
- try {
- initialize();
- } catch (Exception ex) {
- ex.printStackTrace();
- }
- } else {
- try {
- DocumentBuilderFactory domFactory = FactoryProvider.getDocumentBuilderFactory();
- domFactory.setNamespaceAware(true);
- DocumentBuilder domBuilder = domFactory.newDocumentBuilder();
- Document config = domBuilder.parse(configFile);
- XPathFactory xpf = XPathFactory.newInstance();
- XPath xp = xpf.newXPath();
- XPath xp2 = xpf.newXPath();
- String sStopPort = (String) xp.evaluate("/batches/@stop-listen-port", config, XPathConstants.STRING);
- int stopPort = DEFAULT_STOP_PORT;
- try {
- stopPort = Integer.parseInt(sStopPort);
- } catch (NumberFormatException nfEx) {
- throw new Exception("config file contains an invalid stop-port number");
- }
- stop(stopPort);
- } catch (Exception ex) {
- ex.printStackTrace();
- }
- }
- }
-
- @SuppressWarnings("unchecked")
- protected void initialize() throws Exception {
- DocumentBuilderFactory domFactory = FactoryProvider.getDocumentBuilderFactory();
- domFactory.setNamespaceAware(true);
- DocumentBuilder domBuilder = domFactory.newDocumentBuilder();
- Document config = domBuilder.parse(configFile);
- XPathFactory xpf = FactoryProvider.getXPathFactory();
- XPath xp = xpf.newXPath();
- XPath xp2 = xpf.newXPath();
- timers = new ArrayList<MyTimer>();
- String sStopPort = (String) xp.evaluate("/batches/@stop-listen-port", config, XPathConstants.STRING);
- int stopPort = DEFAULT_STOP_PORT;
- try {
- stopPort = Integer.parseInt(sStopPort);
- } catch (NumberFormatException nfEx) {
- throw new Exception("config file contains an invalid stop-port number");
- }
- String allowedStoppers = (String) xp.evaluate("/batches/@allowedStoppers", config, XPathConstants.STRING);
- try {
- startStopListener(stopPort,allowedStoppers);
- } catch(Exception ex) {
- // si on a pas pu démarrer le listener, on quitte
- for(Timer tm:timers) {
- tm.cancel();
- tm.purge();
- }
- return;
- }
- // on demarre le infoListener
- String sInfoPort = (String) xp.evaluate("/batches/@info-listen-port", config, XPathConstants.STRING);
- int infoPort = DEFAULT_INFO_PORT;
- try {
- infoPort = Integer.parseInt(sInfoPort);
- } catch (NumberFormatException nfEx) {
- Exception ex = new Exception("config file contains an invalid info-port number");
- logger.error("while reading info-port number",ex);
- }
- try {
- startInfoListener(infoPort);
- } catch(Exception ex) {
- // si on a pas pu démarrer le listener, on logge
- logger.error("while starting info listener",ex);
- }
-
- // on charge la configuration des documents
- String documentsDefDir = (String)xp.evaluate("/batches/parameter[@name='documents-def.dir']/text()", config, XPathConstants.STRING);
- initDocuments(documentsDefDir);
-// Loader.getDocumentsInfos(documentsDefDir);
-
- // on initialise le DataLayer
- initDataLayer();
-
- Object ret = xp.evaluate("/batches/batch[not(exists(@periodicity))]", config, XPathConstants.NODESET);
- if (ret instanceof NodeList) {
- NodeList nl = (NodeList)ret;
- for( int i=0;i<nl.getLength(); i++) {
- Element batch = (Element)nl.item(i);
- if(batch.hasAttribute("if")) {
- String ifPropertyName = batch.getAttribute("if");
- if(!"true".equals(applicationConfiguration.getProperty(ifPropertyName)))
- continue;
- }
- String className = batch.getAttribute("class");
- Properties props = new Properties();
- NodeList parameters = (NodeList) xp2.evaluate("./parameter", batch, XPathConstants.NODESET);
- for (int pCount=0; pCount<parameters.getLength(); pCount++) {
- Element parameter = (Element)parameters.item(pCount);
- String pName = parameter.getAttribute("name");
- String pValue = null;
- if (parameter.hasAttribute("value")) {
- pValue = parameter.getAttribute("value");
- } else {
- pValue = parameter.getTextContent();
- }
- if (pName != null && pValue != null) {
- props.setProperty(pName, pValue);
- }
- }
- NodeList postProcessors = (NodeList) xp2.evaluate("./post-processor", batch, XPathConstants.NODESET);
- ArrayList<String> postProcessorsClasses = new ArrayList<String>();
- for(int ppCount = 0; ppCount < postProcessors.getLength(); ppCount++) {
- Element pp = (Element)postProcessors.item(ppCount);
- postProcessorsClasses.add(pp.getTextContent());
- }
- startNewBatch(className, /*periodicity, startTime,*/ props, postProcessorsClasses);
- }
-// } else if (ret instanceof List) {
-// for (Element batch : (List<Element>) ret) {
-// }
- }
-
-
- ret = xp.evaluate("/batches/batch[@periodicity]", config, XPathConstants.NODESET);
- if (ret instanceof NodeList) {
- NodeList nl = (NodeList)ret;
- for (int i=0; i<nl.getLength(); i++) {
- Element batch = (Element)nl.item(i);
- if(batch.hasAttribute("if")) {
- String ifProperty = batch.getAttribute("if");
- if(!"true".equals(applicationConfiguration.getProperty(ifProperty)))
- continue;
- }
- String className = batch.getAttribute("class");
- String periodicity = batch.getAttribute("periodicity");
- String startTime = batch.getAttribute("start-time");
- Properties props = new Properties();
- NodeList parameters = (NodeList) xp2.evaluate("./parameter", batch, XPathConstants.NODESET);
- for (int j=0;j<parameters.getLength();j++) {
- Element parameter = (Element)parameters.item(j);
- String pName = parameter.getAttribute("name");
- String pValue = null;
- if (parameter.hasAttribute("value")) {
- pValue = parameter.getAttribute("value");
- } else {
- pValue = parameter.getTextContent();
- }
- if (pName != null && pValue != null) {
- props.setProperty(pName, pValue);
- }
- }
- NodeList postProcessors = (NodeList) xp2.evaluate("./post-processor", batch, XPathConstants.NODESET);
- ArrayList<String> postProcessorsClasses = new ArrayList<String>();
- for(int k=0;k<postProcessors.getLength();k++) {
- Element el = (Element)postProcessors.item(k);
- postProcessorsClasses.add(el.getTextContent());
- }
- startNewBatch(className, periodicity, startTime, props, postProcessorsClasses);
- }
-// } else if (ret instanceof List) {
- }
-
- }
-
- public static String getCanonicalBaseDir() throws IOException {
- File baseDir = BatchRunner.getBaseDir();
- if(baseDir.getAbsolutePath().endsWith("bin"))
- baseDir = baseDir.getParentFile();
- String canon = baseDir.getCanonicalPath().replace("\\", "/");
- return canon;
- }
-
- protected void initDocuments(String dirs) {
- try {
-// File baseDir = BatchRunner.getBaseDir();
- String canon = getCanonicalBaseDir();
- String documentsDefDirs = dirs.replaceAll("%basedir%", canon);
- documents = Loader.getDocumentsInfos(documentsDefDirs);
- } catch(Exception ex) {
- System.err.println("Failed to load documents configuration from "+dirs);
- ex.printStackTrace();
- }
- }
-
- protected void startNewBatch(String className, String periodicity, String startTime, Properties props, ArrayList<String> postProcessors) {
- if(cancelled) return;
- try {
- Class clazz = Class.forName(className);
- TimerBatchTask task = new TimerBatchTask(clazz, props, postProcessors);
- char unity = periodicity.charAt(periodicity.length() - 1);
- int qty = Integer.parseInt(periodicity.substring(0, periodicity.length() - 1));
- long period = 0;
- switch (unity) {
- case 's': {
- period = qty * 1000;
- break;
- }
- case 'm': {
- period = qty * 1000 * 60;
- break;
- }
- case 'h': {
- period = qty * 1000 * 60 * 60;
- break;
- }
- case 'd': {
- period = qty * 1000 * 60 * 60 * 24;
- break;
- }
- default:
- throw new RuntimeException(periodicity + " is not a valid periodicity. Periodicity must be a quantity followed by 's', 'm', 'h' or 'd'");
- }
- if(startTime==null || startTime.length()==0) {
- try {
- MyTimer timer = new MyTimer(className);
- timers.add(timer);
- timer.scheduleAtFixedRate(task, 1000, period);
- } catch(Exception ex) {
- System.err.println("ERROR while starting "+task.batchClass.getName());
- }
- } else {
- Date d = sdf.parse(startTime);
- GregorianCalendar gc = new GregorianCalendar();
- GregorianCalendar thisMorning = new GregorianCalendar(gc.get(Calendar.YEAR),gc.get(Calendar.MONTH),gc.get(Calendar.DAY_OF_MONTH));
- Date start = new Date(thisMorning.getTimeInMillis()+d.getTime()+thisMorning.getTimeZone().getDSTSavings());
- MyTimer timer = new MyTimer(className);
- timers.add(timer);
- timer.scheduleAtFixedRate(task, start, period);
- }
- } catch (Exception ex) {
- ex.printStackTrace();
- throw new RuntimeException(ex);
- }
- }
- protected void startNewBatch(String className, Properties props, ArrayList<String> postProcessors) {
- if(cancelled) return;
- try {
- Class clazz = Class.forName(className);
- Constructor<Batch> cstr = clazz.getConstructor(String[].class);
- Batch batch = cstr.newInstance(new Object[]{new String[]{}});
- batch.setProps(props);
- batch.setPostProcessors(postProcessors);
- batch.setApplicationConfiguration(applicationConfiguration);
- batch.launchBatch();
- } catch (Exception ex) {
- ex.printStackTrace();
- throw new RuntimeException(ex);
- }
- }
-
- public static void displaySyntax() {
- StringBuilder sb = new StringBuilder();
- sb.append("java ").append(BatchRunner.class.getName()).append(" <config-file> [-stop]\n\twhere <config-file> is the fully-qualified location of config file.");
- System.err.println(sb.toString());
- }
-
- public void stop(int port) throws Exception {
- logger.info("Stopping batch server");
- Socket so = new Socket("localhost", port);
- BufferedReader br = new BufferedReader(new InputStreamReader(so.getInputStream()));
- PrintWriter pw = new PrintWriter(so.getOutputStream(), true);
- String resp = br.readLine();
- if (resp.startsWith("+OK")) {
- pw.println("stop");
- resp = br.readLine();
- } else {
- logger.error("batch server has respond : " +resp);
- }
- so.close();
- }
-
- protected void preInit() throws Exception {
- // On initialise dans un premier temps les logs :
- File log4jConfiFile = getConfFile("log4j.conf.file", "log4j.xml");
- if (log4jConfiFile == null) {
- throw new Exception("Fichier de configuration de log4j introuvable");
- }
- DOMConfigurator.configure(log4jConfiFile.toString());
-// initDataLayer();
-// logger.debug("datalayer initialized");
- }
- protected void initDataLayer() throws DataConfigurationException {
- try {
- DataLayerManager.getImplementation();
- } catch(DataConfigurationException dcEx) {
- String availableLayers = applicationConfiguration.getProperty(Constants.SYS_PROP_AVAILABLE_LAYERS);
- if(availableLayers==null) availableLayers = "fr.gouv.finances.cp.xemelios.data.impl.MySqlDataLayer";
- StringTokenizer tokenizer = new StringTokenizer(availableLayers, ",");
- while (tokenizer.hasMoreTokens()) {
- String className = tokenizer.nextToken();
- try {
- Class.forName(className);
- } catch (ClassNotFoundException cnfEx) {
- logger.error("instanciating data layer: ", cnfEx);
- }
- }
- // aller chercher les propriétés dans le mysql.properties
-
- DataLayerManager.setApplicationProperties(applicationConfiguration);
- String layerName = applicationConfiguration.getProperty(Constants.SYS_PROP_DATA_IMPL);
- if(layerName==null) layerName = "mysql";
- DataLayerManager.setDataImpl(layerName);
-// // on interdit le cache des configs de persistence
-// DataLayerManager.getImplementation().setUseCachedPersistence(false);
- }
- }
-
- protected File getConfFile(String fileNameSystemProperty, String fileNameIfSystemPropertyNotFound) {
- return getConfFile(fileNameSystemProperty, fileNameIfSystemPropertyNotFound, fileNameIfSystemPropertyNotFound);
- }
-
- protected File getConfFile(String fileNameSystemProperty, String fileNameIfSystemPropertyNotFound, String errorDisplayName) {
- String confFileName = System.getProperty(fileNameSystemProperty);
- File confFile = null;
- if (confFileName == null) {
- confFileName = "conf" + File.separator + fileNameIfSystemPropertyNotFound;
- confFile = new File(confFileName);
- if (!confFile.exists()) {
- confFileName = fileNameIfSystemPropertyNotFound;
- confFile = new File(confFileName);
- if (!confFile.exists()) {
- return null;
- }
- }
- } else {
- confFile = new File(confFileName);
- if (!confFile.exists()) {
- return null;
- }
- }
- return confFile;
- }
-
- protected void startInfoListener(int port) throws Exception {
- infoListener = new BatchInformationListener(this,port);
- }
-
- protected void startStopListener(int port, String allowedStoppers) throws Exception {
- StopServer stopServer = new StopServer(port, timers, allowedStoppers);
- try {
- stopServer.start();
- } catch (Exception ex) {
- ex.printStackTrace();
- throw new RuntimeException("an other instance is already running.");
- }
- }
-
- public static File getBaseDir() {
- String runDir = System.getProperty("user.dir");
- File f = new File(runDir);
- // dans le cas d'un deploy batch
- if(f.getName().equals("bin")) return f.getParentFile();
- // dans le cas d'execution dans IDE
- else return f;
- }
-
- public static BatchRunner getInstance() { return instance; }
- public void registerBatch(Batch batch) {
- logger.info("registring batch "+batch.typeTraitementRefCode());
- runningBatches.add(batch);
- }
- public void unregisterBatch(Batch batch) {
- runningBatches.remove(batch);
- logger.info("unregistring batch "+batch.typeTraitementRefCode());
- }
- public String getInformations() {
- StringBuilder sb = new StringBuilder();
- for(Batch batch:runningBatches) {
- sb.append(batch.getInformations());
- }
- return sb.toString();
- }
- public class TimerBatchTask extends TimerTask {
-
- private Class batchClass;
- private Properties props;
- private List<String> postProcessors;
-
- public TimerBatchTask(Class clazz, Properties props, List<String> postProcessors) {
- super();
- this.batchClass = clazz;
- this.props = props;
- this.postProcessors=postProcessors;
- }
-
- @Override
- public void run() {
- try {
- @SuppressWarnings("unchecked")
- // TODO : vérifier que la précédente instance a finit
- Constructor<Batch> cstr = batchClass.getConstructor(String[].class);
- Batch batch = cstr.newInstance(new Object[]{new String[]{}});
- batch.setApplicationConfiguration(applicationConfiguration);
- batch.setProps(props);
- batch.setPostProcessors(postProcessors);
- batch.launchBatch();
- } catch (Exception ex) {
- ex.printStackTrace();
- throw new RuntimeException(ex);
- }
- }
- }
-
- /**
- * Renvoie la configuration des documents
- * @return
- */
- public DocumentsModel getDocuments () {
- return documents;
- }
+ private static final Logger logger = Logger.getLogger(BatchRunner.class);
+ public static final String DATE_FORMAT = "HH:mm";
+ private static final SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT);
+ private static final SimpleDateFormat debugSdf = new SimpleDateFormat("yyyy/MM/dd HH:mm");
+
+ public static final int DEFAULT_STOP_PORT = 8006;
+ public static final int DEFAULT_INFO_PORT = 8007;
+ private File configFile;
+ private ArrayList<MyTimer> timers;
+
+ private static BatchRunner instance;
+ private ArrayList<Batch> runningBatches;
+ private BatchInformationListener infoListener = null;
+ private final boolean cancelled = false;
+
+ private DocumentsModel documents;
+
+ private PropertiesExpansion applicationConfiguration;
+
+ /**
+ * Point d'entrée Cette classe est lancée avec l'environnement définit en propriétés système. On les récupères pour faire un
+ * <code>applicationConfiguration</code> et le transmettre au besoin.
+ *
+ * @param args
+ */
+ public static void main(final String args[]) {
+ if (args.length == 0) {
+ displaySyntax();
+ throw new RuntimeException("invalid syntax");
+ }
+ final File configFileName = new File(args[0]);
+ if (!configFileName.exists()) {
+ displaySyntax();
+ throw new RuntimeException("config file not found: " + configFileName.getAbsolutePath());
+ }
+ if (args.length == 2 && "-stop".equals(args[1])) {
+ new BatchRunner(configFileName, true);
+ } else {
+ new BatchRunner(configFileName);
+ }
+ }
+
+ public BatchRunner(final File configFile) {
+ super();
+ this.applicationConfiguration = restoreProperties();
+ if (instance == null) {
+ instance = this;
+ this.runningBatches = new ArrayList<Batch>();
+ }
+ this.configFile = configFile;
+ try {
+ this.preInit();
+ } catch (final Throwable t) {
+ t.printStackTrace();
+ return;
+ }
+ try {
+ this.initialize();
+ } catch (final Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+
+ private static PropertiesExpansion restoreProperties() {
+ final PropertiesExpansion applicationConfiguration = new PropertiesExpansion(System.getProperties());
+ // on va lire le mysql.properties
+ // File f = new File(applicationConfiguration.getProperty(Constants.))
+ return applicationConfiguration;
+ }
+
+ public BatchRunner(final File configFile, final boolean stop) {
+ super();
+ this.applicationConfiguration = restoreProperties();
+ if (instance == null) {
+ instance = this;
+ this.runningBatches = new ArrayList<Batch>();
+ }
+ this.configFile = configFile;
+ try {
+ this.preInit();
+ } catch (final Throwable t) {
+ return;
+ }
+ if (!stop) {
+ try {
+ this.initialize();
+ } catch (final Exception ex) {
+ ex.printStackTrace();
+ }
+ } else {
+ try {
+ final DocumentBuilderFactory domFactory = FactoryProvider.getDocumentBuilderFactory();
+ domFactory.setNamespaceAware(true);
+ final DocumentBuilder domBuilder = domFactory.newDocumentBuilder();
+ final Document config = domBuilder.parse(configFile);
+ final XPathFactory xpf = XPathFactory.newInstance();
+ final XPath xp = xpf.newXPath();
+ final XPath xp2 = xpf.newXPath();
+ final String sStopPort = (String) xp.evaluate("/batches/@stop-listen-port", config, XPathConstants.STRING);
+ int stopPort = DEFAULT_STOP_PORT;
+ try {
+ stopPort = Integer.parseInt(sStopPort);
+ } catch (final NumberFormatException nfEx) {
+ throw new Exception("config file contains an invalid stop-port number");
+ }
+ this.stop(stopPort);
+ } catch (final Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ protected void initialize() throws Exception {
+ final DocumentBuilderFactory domFactory = FactoryProvider.getDocumentBuilderFactory();
+ domFactory.setNamespaceAware(true);
+ final DocumentBuilder domBuilder = domFactory.newDocumentBuilder();
+ final Document config = domBuilder.parse(this.configFile);
+ final XPathFactory xpf = FactoryProvider.getXPathFactory();
+ final XPath xp = xpf.newXPath();
+ final XPath xp2 = xpf.newXPath();
+ this.timers = new ArrayList<MyTimer>();
+ final String sStopPort = (String) xp.evaluate("/batches/@stop-listen-port", config, XPathConstants.STRING);
+ int stopPort = DEFAULT_STOP_PORT;
+ try {
+ stopPort = Integer.parseInt(sStopPort);
+ } catch (final NumberFormatException nfEx) {
+ throw new Exception("config file contains an invalid stop-port number");
+ }
+ final String allowedStoppers = (String) xp.evaluate("/batches/@allowedStoppers", config, XPathConstants.STRING);
+ try {
+ this.startStopListener(stopPort, allowedStoppers);
+ } catch (final Exception ex) {
+ // si on a pas pu démarrer le listener, on quitte
+ for (final Timer tm : this.timers) {
+ tm.cancel();
+ tm.purge();
+ }
+ return;
+ }
+ // on demarre le infoListener
+ final String sInfoPort = (String) xp.evaluate("/batches/@info-listen-port", config, XPathConstants.STRING);
+ int infoPort = DEFAULT_INFO_PORT;
+ try {
+ infoPort = Integer.parseInt(sInfoPort);
+ } catch (final NumberFormatException nfEx) {
+ final Exception ex = new Exception("config file contains an invalid info-port number");
+ logger.error("while reading info-port number", ex);
+ }
+ try {
+ this.startInfoListener(infoPort);
+ } catch (final Exception ex) {
+ // si on a pas pu démarrer le listener, on logge
+ logger.error("while starting info listener", ex);
+ }
+
+ // on charge la configuration des documents
+ final String documentsDefDir = (String) xp.evaluate("/batches/parameter[@name='documents-def.dir']/text()", config, XPathConstants.STRING);
+ this.initDocuments(documentsDefDir);
+ // Loader.getDocumentsInfos(documentsDefDir);
+
+ // on initialise le DataLayer
+ this.initDataLayer();
+
+ Object ret = xp.evaluate("/batches/batch[not(exists(@periodicity))]", config, XPathConstants.NODESET);
+ if (ret instanceof NodeList) {
+ final NodeList nl = (NodeList) ret;
+ for (int i = 0; i < nl.getLength(); i++) {
+ final Element batch = (Element) nl.item(i);
+ if (batch.hasAttribute("if")) {
+ final String ifPropertyName = batch.getAttribute("if");
+ if (!"true".equals(this.applicationConfiguration.getProperty(ifPropertyName))) {
+ continue;
+ }
+ }
+ final String className = batch.getAttribute("class");
+ final Properties props = new Properties();
+ final NodeList parameters = (NodeList) xp2.evaluate("./parameter", batch, XPathConstants.NODESET);
+ for (int pCount = 0; pCount < parameters.getLength(); pCount++) {
+ final Element parameter = (Element) parameters.item(pCount);
+ final String pName = parameter.getAttribute("name");
+ String pValue = null;
+ if (parameter.hasAttribute("value")) {
+ pValue = parameter.getAttribute("value");
+ } else {
+ pValue = parameter.getTextContent();
+ }
+ if (pName != null && pValue != null) {
+ props.setProperty(pName, pValue);
+ }
+ }
+ final NodeList postProcessors = (NodeList) xp2.evaluate("./post-processor", batch, XPathConstants.NODESET);
+ final ArrayList<String> postProcessorsClasses = new ArrayList<String>();
+ for (int ppCount = 0; ppCount < postProcessors.getLength(); ppCount++) {
+ final Element pp = (Element) postProcessors.item(ppCount);
+ postProcessorsClasses.add(pp.getTextContent());
+ }
+ this.startNewBatch(className, /* periodicity, startTime, */props, postProcessorsClasses);
+ }
+ // } else if (ret instanceof List) {
+ // for (Element batch : (List<Element>) ret) {
+ // }
+ }
+
+ ret = xp.evaluate("/batches/batch[@periodicity]", config, XPathConstants.NODESET);
+ if (ret instanceof NodeList) {
+ final NodeList nl = (NodeList) ret;
+ for (int i = 0; i < nl.getLength(); i++) {
+ final Element batch = (Element) nl.item(i);
+ if (batch.hasAttribute("if")) {
+ final String ifProperty = batch.getAttribute("if");
+ if (!"true".equals(this.applicationConfiguration.getProperty(ifProperty))) {
+ continue;
+ }
+ }
+ final String className = batch.getAttribute("class");
+ final String periodicity = batch.getAttribute("periodicity");
+ final String startTime = batch.getAttribute("start-time");
+ final Properties props = new Properties();
+ final NodeList parameters = (NodeList) xp2.evaluate("./parameter", batch, XPathConstants.NODESET);
+ for (int j = 0; j < parameters.getLength(); j++) {
+ final Element parameter = (Element) parameters.item(j);
+ final String pName = parameter.getAttribute("name");
+ String pValue = null;
+ if (parameter.hasAttribute("value")) {
+ pValue = parameter.getAttribute("value");
+ } else {
+ pValue = parameter.getTextContent();
+ }
+ if (pName != null && pValue != null) {
+ props.setProperty(pName, pValue);
+ }
+ }
+ final NodeList postProcessors = (NodeList) xp2.evaluate("./post-processor", batch, XPathConstants.NODESET);
+ final ArrayList<String> postProcessorsClasses = new ArrayList<String>();
+ for (int k = 0; k < postProcessors.getLength(); k++) {
+ final Element el = (Element) postProcessors.item(k);
+ postProcessorsClasses.add(el.getTextContent());
+ }
+ this.startNewBatch(className, periodicity, startTime, props, postProcessorsClasses);
+ }
+ // } else if (ret instanceof List) {
+ }
+
+ }
+
+ public static String getCanonicalBaseDir() throws IOException {
+ File baseDir = BatchRunner.getBaseDir();
+ if (baseDir.getAbsolutePath().endsWith("bin")) {
+ baseDir = baseDir.getParentFile();
+ }
+ final String canon = baseDir.getCanonicalPath().replace("\\", "/");
+ return canon;
+ }
+
+ protected void initDocuments(final String dirs) {
+ try {
+ // File baseDir = BatchRunner.getBaseDir();
+ final String canon = getCanonicalBaseDir();
+ final String documentsDefDirs = dirs.replaceAll("%basedir%", canon);
+ this.documents = Loader.getDocumentsInfos(documentsDefDirs);
+ } catch (final Exception ex) {
+ System.err.println("Failed to load documents configuration from " + dirs);
+ ex.printStackTrace();
+ }
+ }
+
+ protected void startNewBatch(final String className, final String periodicity, final String startTime, final Properties props, final ArrayList<String> postProcessors) {
+ if (this.cancelled) {
+ return;
+ }
+ try {
+ final Class clazz = Class.forName(className);
+ final TimerBatchTask task = new TimerBatchTask(clazz, props, postProcessors);
+ final char unity = periodicity.charAt(periodicity.length() - 1);
+ final int qty = Integer.parseInt(periodicity.substring(0, periodicity.length() - 1));
+ long period = 0;
+ switch (unity) {
+ case 's': {
+ period = qty * 1000;
+ break;
+ }
+ case 'm': {
+ period = qty * 1000 * 60;
+ break;
+ }
+ case 'h': {
+ period = qty * 1000 * 60 * 60;
+ break;
+ }
+ case 'd': {
+ period = qty * 1000 * 60 * 60 * 24;
+ break;
+ }
+ default:
+ throw new RuntimeException(periodicity + " is not a valid periodicity. Periodicity must be a quantity followed by 's', 'm', 'h' or 'd'");
+ }
+ if (startTime == null || startTime.length() == 0) {
+ try {
+ final MyTimer timer = new MyTimer(className);
+ this.timers.add(timer);
+ timer.scheduleAtFixedRate(task, 1000, period);
+ } catch (final Exception ex) {
+ System.err.println("ERROR while starting " + task.batchClass.getName());
+ }
+ } else {
+ final Date d = sdf.parse(startTime);
+ final GregorianCalendar gc = new GregorianCalendar();
+ final GregorianCalendar thisMorning = new GregorianCalendar(gc.get(Calendar.YEAR), gc.get(Calendar.MONTH), gc.get(Calendar.DAY_OF_MONTH));
+ final Date start = new Date(thisMorning.getTimeInMillis() + d.getTime() + thisMorning.getTimeZone().getDSTSavings());
+ final MyTimer timer = new MyTimer(className);
+ this.timers.add(timer);
+ timer.scheduleAtFixedRate(task, start, period);
+ }
+ } catch (final Exception ex) {
+ ex.printStackTrace();
+ throw new RuntimeException(ex);
+ }
+ }
+
+ protected void startNewBatch(final String className, final Properties props, final ArrayList<String> postProcessors) {
+ if (this.cancelled) {
+ return;
+ }
+ try {
+ final Class clazz = Class.forName(className);
+ final Constructor<Batch> cstr = clazz.getConstructor(String[].class);
+ final Batch batch = cstr.newInstance(new Object[] { new String[] {} });
+ batch.setProps(props);
+ batch.setPostProcessors(postProcessors);
+ batch.setApplicationConfiguration(this.applicationConfiguration);
+ batch.launchBatch();
+ } catch (final Exception ex) {
+ ex.printStackTrace();
+ throw new RuntimeException(ex);
+ }
+ }
+
+ public static void displaySyntax() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("java ").append(BatchRunner.class.getName()).append(" <config-file> [-stop]\n\twhere <config-file> is the fully-qualified location of config file.");
+ System.err.println(sb.toString());
+ }
+
+ public void stop(final int port) throws Exception {
+ logger.info("Stopping batch server");
+ final Socket so = new Socket("localhost", port);
+ final BufferedReader br = new BufferedReader(new InputStreamReader(so.getInputStream()));
+ final PrintWriter pw = new PrintWriter(so.getOutputStream(), true);
+ String resp = br.readLine();
+ if (resp.startsWith("+OK")) {
+ pw.println("stop");
+ resp = br.readLine();
+ } else {
+ logger.error("batch server has respond : " + resp);
+ }
+ so.close();
+ }
+
+ protected void preInit() throws Exception {
+ // On initialise dans un premier temps les logs :
+ final File log4jConfiFile = this.getConfFile("log4j.conf.file", "log4j.xml");
+ if (log4jConfiFile == null) {
+ throw new Exception("Fichier de configuration de log4j introuvable");
+ }
+ DOMConfigurator.configure(log4jConfiFile.toString());
+ // initDataLayer();
+ // logger.debug("datalayer initialized");
+ }
+
+ protected void initDataLayer() throws DataConfigurationException {
+ try {
+ DataLayerManager.getImplementation();
+ } catch (final DataConfigurationException dcEx) {
+ String availableLayers = this.applicationConfiguration.getProperty(Constants.SYS_PROP_AVAILABLE_LAYERS);
+ if (availableLayers == null) {
+ availableLayers = "fr.gouv.finances.cp.xemelios.data.impl.MySqlDataLayer";
+ }
+ final StringTokenizer tokenizer = new StringTokenizer(availableLayers, ",");
+ while (tokenizer.hasMoreTokens()) {
+ final String className = tokenizer.nextToken();
+ try {
+ Class.forName(className);
+ } catch (final ClassNotFoundException cnfEx) {
+ logger.error("instanciating data layer: ", cnfEx);
+ }
+ }
+ // aller chercher les propriétés dans le mysql.properties
+
+ DataLayerManager.setApplicationProperties(this.applicationConfiguration);
+ String layerName = this.applicationConfiguration.getProperty(Constants.SYS_PROP_DATA_IMPL);
+ if (layerName == null) {
+ layerName = "mysql";
+ }
+ DataLayerManager.setDataImpl(layerName);
+ // // on interdit le cache des configs de persistence
+ // DataLayerManager.getImplementation().setUseCachedPersistence(false);
+ }
+ }
+
+ protected File getConfFile(final String fileNameSystemProperty, final String fileNameIfSystemPropertyNotFound) {
+ return this.getConfFile(fileNameSystemProperty, fileNameIfSystemPropertyNotFound, fileNameIfSystemPropertyNotFound);
+ }
+
+ protected File getConfFile(final String fileNameSystemProperty, final String fileNameIfSystemPropertyNotFound, final String errorDisplayName) {
+ String confFileName = System.getProperty(fileNameSystemProperty);
+ File confFile = null;
+ if (confFileName == null) {
+ confFileName = "conf" + File.separator + fileNameIfSystemPropertyNotFound;
+ confFile = new File(confFileName);
+ if (!confFile.exists()) {
+ confFileName = fileNameIfSystemPropertyNotFound;
+ confFile = new File(confFileName);
+ if (!confFile.exists()) {
+ return null;
+ }
+ }
+ } else {
+ confFile = new File(confFileName);
+ if (!confFile.exists()) {
+ return null;
+ }
+ }
+ return confFile;
+ }
+
+ protected void startInfoListener(final int port) throws Exception {
+ this.infoListener = new BatchInformationListener(this, port);
+ }
+
+ protected void startStopListener(final int port, final String allowedStoppers) throws Exception {
+ final StopServer stopServer = new StopServer(port, this.timers, allowedStoppers);
+ try {
+ stopServer.start();
+ } catch (final Exception ex) {
+ ex.printStackTrace();
+ throw new RuntimeException("an other instance is already running.");
+ }
+ }
+
+ public static File getBaseDir() {
+ final String runDir = System.getProperty("user.dir");
+ final File f = new File(runDir);
+ // dans le cas d'un deploy batch
+ if (f.getName().equals("bin")) {
+ return f.getParentFile();
+ } else {
+ return f;
+ }
+ }
+
+ public static BatchRunner getInstance() {
+ return instance;
+ }
+
+ public void registerBatch(final Batch batch) {
+ logger.info("registring batch " + batch.typeTraitementRefCode());
+ this.runningBatches.add(batch);
+ }
+
+ public void unregisterBatch(final Batch batch) {
+ this.runningBatches.remove(batch);
+ logger.info("unregistring batch " + batch.typeTraitementRefCode());
+ }
+
+ public String getInformations() {
+ final StringBuilder sb = new StringBuilder();
+ for (final Batch batch : this.runningBatches) {
+ sb.append(batch.getInformations());
+ }
+ return sb.toString();
+ }
+
+ public class TimerBatchTask extends TimerTask {
+
+ private final Class batchClass;
+ private final Properties props;
+ private final List<String> postProcessors;
+
+ public TimerBatchTask(final Class clazz, final Properties props, final List<String> postProcessors) {
+ super();
+ this.batchClass = clazz;
+ this.props = props;
+ this.postProcessors = postProcessors;
+ }
+
+ @Override
+ public void run() {
+ try {
+ @SuppressWarnings("unchecked")
+ final// TODO : vérifier que la précédente instance a finit
+ Constructor<Batch> cstr = this.batchClass.getConstructor(String[].class);
+ final Batch batch = cstr.newInstance(new Object[] { new String[] {} });
+ batch.setApplicationConfiguration(BatchRunner.this.applicationConfiguration);
+ batch.setProps(this.props);
+ batch.setPostProcessors(this.postProcessors);
+ batch.launchBatch();
+ } catch (final Exception ex) {
+ ex.printStackTrace();
+ throw new RuntimeException(ex);
+ }
+ }
+ }
+
+ /**
+ * Renvoie la configuration des documents
+ *
+ * @return
+ */
+ public DocumentsModel getDocuments() {
+ return this.documents;
+ }
}