/* * * YAQP - Yet Another QSAR Project: * Machine Learning algorithms designed for the prediction of toxicological * features of chemical compounds become available on the Web. Yaqp is developed * under OpenTox (http://opentox.org) which is an FP7-funded EU research project. * This project was developed at the Automatic Control Lab in the Chemical Engineering * School of the National Technical University of Athens. Please read README for more * information. * * Copyright (C) 2009-2010 Pantelis Sopasakis & Charalampos Chomenides * * 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 3 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/>. * * Contact: * Pantelis Sopasakis * chvng@mail.ntua.gr * Address: Iroon Politechniou St. 9, Zografou, Athens Greece * tel. +30 210 7723236 */ package org.opentox.util.monitoring; import java.io.IOException; import java.net.InetAddress; import java.net.Socket; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; import org.opentox.db.exceptions.DbException; import org.opentox.db.exceptions.DuplicateKeyException; import org.opentox.db.handlers.WriterHandler; import org.opentox.db.table.StandardTables; import org.opentox.db.table.collection.UsersTable; import org.opentox.db.util.TheDbConnector; import org.opentox.ontology.components.Algorithm; import org.opentox.ontology.components.AlgorithmOntology; import org.opentox.ontology.components.User; import org.opentox.ontology.components.UserGroup; import org.opentox.ontology.exceptions.ImproperEntityException; import org.opentox.ontology.exceptions.YaqpOntException; import org.opentox.ontology.namespaces.OTAlgorithmTypes; import org.opentox.ontology.util.YaqpAlgorithms; import org.opentox.util.logging.YaqpLogger; import org.opentox.util.logging.levels.*; /** * * Let us introduce Jennifer. Jennifer is the head of YAQP. She controls and monitors * the whole system. She's a singleton that periofically checks if the datbase server * (apache derby) is up and running, if the application is properly connected in the database and * if the HTTP server is alive. If some of these operations does not work properly * , Jennifer attemts to fix it (e.g. she restarts the derby server). What is more, * Jenny initializes the database and populates it with valuable data such as an * administrator user, adds algorithms, algorithm ontologies and algorithm-ontology * relations in the corresponding tables of the database. * * @author Pantelis Sopasakis * @author Charalampos Chomenides */ public class Jennifer extends Thread { private static Jennifer instanceOfThis = null; public static Jennifer INSTANCE = getInstance(); // <editor-fold defaultstate="collapsed" desc="CONSTANTS for Jennifer"> private final int PERIOD = 5; private final TimeUnit SCHEDULE_UNIT = TimeUnit.SECONDS; private final int DERBY_RESTART_ATTEMPTS = 5, DERBY_RESTART_DELAY_ms = 500, HTTP_PING_ATTEMPTS = 3; // </editor-fold> private boolean isDatabaseInitialized = false; public boolean isDbInit() { return isDatabaseInitialized; } // <editor-fold defaultstate="collapsed" desc="get the single instance for Jennifer"> private static Jennifer getInstance() { if (instanceOfThis == null) { instanceOfThis = new Jennifer(); } return instanceOfThis; }// </editor-fold> private Jennifer() { setName("jenifer"); } @Override public void run() { initializedDatabase(); isDatabaseInitialized = true; Timer timer = new Timer("YAQP Janitor"); long period = TimeUnit.MILLISECONDS.convert(PERIOD, SCHEDULE_UNIT); timer.schedule(new MONITOR(), period, period); } // <editor-fold defaultstate="collapsed" desc="A scheduled monitor for the whole application"> private class MONITOR extends TimerTask { @Override public void run() { checkDbConnectivity(); checkHttpConnectivity(HTTP_PING_ATTEMPTS); } }// </editor-fold> // <editor-fold defaultstate="collapsed" desc="Initialized the database"> private void initializedDatabase() { try { TheDbConnector.init(); populateAlgorithmOntologies(); populateAlgorithms(); populateUserGroups(); populateUsers(); } catch (DbException ex) { System.out.println(ex); // What to do??? } catch (ExceptionInInitializerError ex) { System.out.println("\n\nIt seems someone is already connected in the database with the same username"); System.exit(3); } }// </editor-fold> // <editor-fold defaultstate="collapsed" desc="populate the ontologies in the database"> private void populateAlgorithmOntologies() throws DbException { try { ArrayList<OTAlgorithmTypes> otlist = OTAlgorithmTypes.getAllAlgorithmTypes(); for (OTAlgorithmTypes ot : otlist) { try { WriterHandler.add(new AlgorithmOntology(ot)); } catch (DbException ex) { if (!(ex instanceof DuplicateKeyException)) { throw ex; } } } } catch (YaqpOntException ex) {/* do nonthing */ } }// </editor-fold> // <editor-fold defaultstate="collapsed" desc="populate the algorithms in the database"> private void populateAlgorithms() throws DbException { for (Algorithm alg : YaqpAlgorithms.getAllAlgorithms()) { try { WriterHandler.add(alg); } catch (DbException ex) { /* do nonthing */ if (!(ex instanceof DuplicateKeyException)) { throw ex; } } catch (YaqpOntException ex) {/* do nonthing */ } } }// </editor-fold> // <editor-fold defaultstate="collapsed" desc="register the standard user groups in the DB"> private void populateUserGroups() throws DbException { try { try { WriterHandler.add(new UserGroup("GUEST", 10,"CCC", "CCC", "CCC" , "CCC", 3000)); } catch (DbException ex) { if (!(ex instanceof DuplicateKeyException)) { YaqpLogger.LOG.log(new ScrewedUp(getClass(), "UserGroup 'GUEST' cannot be added in the database")); throw ex; } } catch (ImproperEntityException ex) { } try { WriterHandler.add(new UserGroup("SIMPLE", 100,"CCC", "CCC", "CCC" , "CCC", 3000)); } catch (DbException ex) { if (!(ex instanceof DuplicateKeyException)) { YaqpLogger.LOG.log(new ScrewedUp(getClass(), "UserGroup 'SIMPLE' cannot be added in the database")); throw ex; } } catch (ImproperEntityException ex) { } try { WriterHandler.add(new UserGroup("ADMINISTRATOR", 1000,"CCC", "CCC", "CCC" , "CCC", 3000)); } catch (DbException ex) { if (!(ex instanceof DuplicateKeyException)) { YaqpLogger.LOG.log(new ScrewedUp(getClass(), "UserGroup 'ADMINISTRATOR' cannot be added in the database")); throw ex; } } catch (ImproperEntityException ex) { } try { WriterHandler.add(new UserGroup("JANITOR", 10000,"CCC", "CCC", "CCC" , "CCC", 3000)); } catch (DbException ex) { if (!(ex instanceof DuplicateKeyException)) { YaqpLogger.LOG.log(new ScrewedUp(getClass(), "UserGroup 'JANITOR' cannot be added in the database")); throw ex; } } } catch (ImproperEntityException ex) { } }// </editor-fold> // <editor-fold defaultstate="collapsed" desc="Write the janitor information in the DB"> private void populateUsers() { String pass = "2331f5f83cdf42c1b08eaead3a1f1e44d7988676faebc2a7f90832ff496da8d6e1527738d29c552a07c1348098570459573925d178415751f211bc485d331d2e"; User u = new User( "chung", pass, "Pantelis", "Sopasakis", "makis@foo.goo.gr", null, "Greece", "Athens", "9, Iroon Politechniou St..", "https://opentox.ntua.gr/new", null, new UserGroup("JANITOR", 10000,"CCC", "CCC", "CCC" , "CCC", 3000)); try { WriterHandler.add(u); } catch (DbException ex) { if (ex instanceof DuplicateKeyException) { YaqpLogger.LOG.log(new Trace(getClass(), "Janitor user already in the database")); } else { throw new RuntimeException("Unexpected condition while trying to add janitor user.", ex); } } catch (ImproperEntityException ex) { Logger.getLogger(Jennifer.class.getName()).log(Level.SEVERE, null, ex); } }// </editor-fold> // <editor-fold defaultstate="collapsed" desc="check the connectivity to the DB"> private void checkDbConnectivity() { // WAIT A LITTLE BEFORE CHECKING IF THE DERBY SERVER IS UP. try { Thread.sleep(DERBY_RESTART_DELAY_ms); } catch (InterruptedException ex) { } // CHECK IF DERBY IS RUNNING. IF NOT, ATTEMPT TO RESTART IT... int i = 0; if (!TheDbConnector.DB.isDerbyRunning() && i < DERBY_RESTART_ATTEMPTS) { try { TheDbConnector.DB.startDerbyServer(); Thread.sleep(DERBY_RESTART_DELAY_ms); } catch (Exception ex) { /* Exception while trying to start the DB server */ } i++; } // CHECK IF IT IS POSSIBLE TO RETRIEVE THE USER 'chung' FROM THE DATABASE: // IF NOT, REESTABLISH THE DB CONNECTION: try { Statement stat = TheDbConnector.DB.getConnection().createStatement(); ResultSet rs = stat.executeQuery("SELECT * FROM " + UsersTable.TABLE.getTableName() + " WHERE " + UsersTable.USERNAME.getColumnName() + " = 'chung'"); while (rs.next()) { assert(rs.getString(1).equals("chung")); } } catch (SQLException ex) { /* SQLException is thrown if the sql statement is * uncompilable or there is no db connection */ refreshDbConnection(); } catch (AssertionError ae){/* * This assertion error is thrown if there was no * user with username 'chung' in the database. */ refreshDbConnection(); } if (TheDbConnector.DB == null || TheDbConnector.DB.getConnection() == null) { refreshDbConnection(); } }// </editor-fold> // <editor-fold defaultstate="collapsed" desc="Disconnect from and reconnect to the database"> private void refreshDbConnection() { YaqpLogger.LOG.log(new Info(getClass(), "Refreshing database connection")); TheDbConnector.DB.disconnect(); try { TheDbConnector.DB.connect(); } catch (Exception ex) { // After all what??? } }// </editor-fold> // <editor-fold defaultstate="collapsed" desc="Check if the Web Server is Alive by pinging localhost"> private void checkHttpConnectivity(int attempts) { java.net.Socket soc = null; int i = 1; boolean isalive = false; while (isalive == false) { if (i == attempts) { break; } try { soc = new Socket(InetAddress.getLocalHost(), 80); isalive = true; } catch (java.io.IOException e) { try { Thread.sleep(500); } catch (InterruptedException ex) { Logger.getLogger(Jennifer.class.getName()).log(Level.SEVERE, null, ex); } i++; isalive = false; } } if (!(soc == null)) { try { soc.close(); } catch (IOException ex) { } } // System.out.println("x" + isalive); if (!isalive) { System.out.println("NOT ALIVE"); } }// </editor-fold> // <editor-fold defaultstate="collapsed" desc="Clear all DB entries without deleting the tables"> public void resetDBContent() throws Exception { StandardTables[] tablesForCleanup = StandardTables.values(); for (int i = tablesForCleanup.length - 1; i >= 0; i--) { try { Statement statement = TheDbConnector.DB.getConnection().createStatement(); String sql = "DELETE FROM " + tablesForCleanup[i].getTable().getTableName(); //System.out.println(sql); statement.executeUpdate(sql); } catch (SQLException ex) { throw ex; } } }// </editor-fold> public void ressurect(){ try { // THE RESSURECTION SCRIPT WAITS FOR 5s BEFORE STARTING YAQP AGAIN. // IN THE MEANWHILE, THE PARENT APPLICATION HAS ENOUGH TIME TO SUICIDE // SO THAT IT WILL BE DIFFICULT THAT TWO INSTANCES OF YAQP RUN AT THE // SAME TIME. IF THE RESSURECTION SCRIPT IS NOT FOUND, THE APPLICATION // JUST EXITS. String[] ressurection = {"sh", "ressurection.sh"}; Runtime.getRuntime().exec(ressurection); Thread.sleep(100); System.exit(4); } catch (InterruptedException ex) { Logger.getLogger(Jennifer.class.getName()).log(Level.SEVERE, null, ex); } catch (IOException ex) { Logger.getLogger(Jennifer.class.getName()).log(Level.SEVERE, null, ex); } finally { System.exit(4); } } }