Home | History | Annotate | Download | only in management
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * See LICENSE.txt included in this distribution for the specific
      9  * language governing permissions and limitations under the License.
     10  *
     11  * When distributing Covered Code, include this CDDL HEADER in each
     12  * file and include the License file at LICENSE.txt.
     13  * If applicable, add the following below this CDDL HEADER, with the
     14  * fields enclosed by brackets "[]" replaced with your own identifying
     15  * information: Portions Copyright [yyyy] [name of copyright owner]
     16  *
     17  * CDDL HEADER END
     18  */
     19 
     20 /*
     21  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
     22  * Use is subject to license terms.
     23  */
     24 package org.opensolaris.opengrok.management;
     25 
     26 import java.io.File;
     27 import java.util.Arrays;
     28 import java.util.HashSet;
     29 import java.util.Iterator;
     30 import java.util.List;
     31 import java.util.Set;
     32 import java.util.logging.Level;
     33 import java.util.logging.Logger;
     34 import javax.management.ListenerNotFoundException;
     35 import javax.management.MBeanNotificationInfo;
     36 import javax.management.MBeanRegistration;
     37 import javax.management.MBeanServer;
     38 import javax.management.Notification;
     39 import javax.management.NotificationEmitter;
     40 import javax.management.NotificationFilter;
     41 import javax.management.NotificationListener;
     42 import javax.management.ObjectName;
     43 import org.opensolaris.opengrok.configuration.RuntimeEnvironment;
     44 import org.opensolaris.opengrok.history.HistoryGuru;
     45 import org.opensolaris.opengrok.index.IndexChangedListener;
     46 import org.opensolaris.opengrok.index.Indexer;
     47 
     48 /**
     49  * AgentIndexRunner.
     50  * @author Jan S Berg
     51  */
     52 public final class AgentIndexRunner implements AgentIndexRunnerMBean, NotificationListener,
     53         MBeanRegistration, Runnable, IndexChangedListener, NotificationEmitter {
     54 
     55     private transient static AgentIndexRunner indexerInstance = null;
     56     private final static String NOTIFICATIONACTIONTYPE = "ogaaction";
     57     private final static String NOTIFICATIONEXCEPTIONTYPE = "ogaexception";
     58     private final static String NOTIFICATIONINFOSTRINGTYPE = "ogainfostring";
     59     private final static String NOTIFICATIONINFOLONGTYPE = "ogainfolong";
     60     private boolean enabled;
     61     private transient Thread indexThread = null;
     62     private final static Logger log = Logger.getLogger("org.opensolaris.opengrok");
     63     private RuntimeEnvironment env = null;
     64     private long lastIndexStart = 0;
     65     private long lastIndexFinish = 0;
     66     private long lastIndexUsedTime = 0;
     67     private Exception lastException = null;
     68     private final Set<NotificationHolder> notifListeners =
     69             new HashSet<NotificationHolder>();
     70     private static long sequenceNo = 0;
     71     private final StringBuilder notifications = new StringBuilder();
     72     private final static int MAXMESSAGELENGTH = 50000;
     73 
     74     /**
     75      * The only constructor is private, so other classes will only get an
     76      * instance through the static factory method getInstance().
     77      */
     78     private AgentIndexRunner(boolean enabledParam) {
     79         enabled = enabledParam;
     80     }
     81 
     82     /**
     83      * Static factory method to get an instance of AgentIndexRunner.
     84      * @param enabledParam if true, the initial instance should be running or not
     85      */
     86     @SuppressWarnings("PMD.AvoidSynchronizedAtMethodLevel")
     87     public static synchronized AgentIndexRunner getInstance(boolean enabledParam) {
     88         if (indexerInstance == null) {
     89             indexerInstance = new AgentIndexRunner(enabledParam);
     90         }
     91         return indexerInstance;
     92     }
     93 
     94     public ObjectName preRegister(MBeanServer serverParam, ObjectName name) {
     95         return name;
     96     }
     97 
     98     public void postRegister(Boolean registrationDone) {
     99         // not used
    100     }
    101 
    102     public void preDeregister() {
    103         // not used
    104     }
    105 
    106     public void postDeregister() {
    107         // not used
    108     }
    109 
    110     public void run() {
    111         try {
    112             //Indexer ind = new Indexer();
    113             log.info("Running...");
    114             lastIndexStart = System.currentTimeMillis();
    115             lastException = null;
    116             doNotify(NOTIFICATIONINFOLONGTYPE, "StartIndexing", Long.valueOf(lastIndexStart));
    117             String configfile = Management.getInstance().getConfigurationFile();
    118             if (configfile == null) {
    119                 doNotify(NOTIFICATIONEXCEPTIONTYPE, "Missing Configuration file", "");
    120             }
    121             File cfgFile = new File(configfile);
    122             if (cfgFile.exists()) {
    123                 env = RuntimeEnvironment.getInstance();
    124                 log.info("Running indexer with configuration " + configfile);
    125                 env.readConfiguration(cfgFile);
    126 
    127                 Indexer index = Indexer.getInstance();
    128                 int noThreads = Management.getInstance().getNumberOfThreads().intValue();
    129                 boolean update = Management.getInstance().getUpdateIndexDatabase().booleanValue();
    130                 String[] sublist = Management.getInstance().getSubFiles();
    131                 log.info("Update source repositories");
    132                 HistoryGuru.getInstance().updateRepositories();
    133                 List<String> subFiles = Arrays.asList(sublist);
    134                 log.info("Starting index, update " + update + " noThreads " + noThreads + " subfiles " + subFiles.size());
    135                 index.doIndexerExecution(update, noThreads, subFiles, this);
    136                 log.info("Finished indexing");
    137                 lastIndexFinish = System.currentTimeMillis();
    138                 sendNotifications();
    139                 doNotify(NOTIFICATIONINFOLONGTYPE, "FinishedIndexing", Long.valueOf(lastIndexFinish));
    140                 lastIndexUsedTime = lastIndexFinish - lastIndexStart;
    141                 String publishhost = Management.getInstance().getPublishServerURL();
    142                 if ((publishhost == null) || (publishhost.equals(""))) {
    143                     log.warning("No publishhost given, not sending updates");
    144                 } else {
    145                     index.sendToConfigHost(env, publishhost);
    146                     doNotify(NOTIFICATIONINFOSTRINGTYPE, "Published index", publishhost);
    147                 }
    148 
    149 
    150             } else {
    151                 log.warning("Cannot Run indexing without proper configuration file " + configfile);
    152                 doNotify(NOTIFICATIONEXCEPTIONTYPE, "Configuration file not valid", configfile);
    153             }
    154         } catch (Exception e) {
    155             log.log(Level.SEVERE,
    156                     "Exception running indexing ", e);
    157             lastException = e;
    158         }
    159     }
    160 
    161     /**
    162      * Disables indexer
    163      */
    164     public void disable() {
    165         enabled = false;
    166     }
    167 
    168     /**
    169      * Enables the indexer
    170      */
    171     public void enable() {
    172         enabled = true;
    173     }
    174 
    175     /**
    176      * Handle timer notifications to the purgatory.
    177      * Will start the purger if it is enabled and return immediately.
    178      */
    179     public void handleNotification(Notification n, Object hb) {
    180         if (n.getType().equals("timer.notification")) {
    181             log.finer("Received timer notification");
    182             if (enabled) {
    183                 index(false);
    184             } else {
    185                 log.info("Indexing is disabled, doing nothing");
    186             }
    187         } else {
    188             log.warning("Received unknown notification type: " + n.getType());
    189         }
    190     }
    191 
    192     /**
    193      * The index method starts a thread that will
    194      * start indexing part of the opengrok agent.
    195      * @param waitForFinished if false the command returns immediately, if true
    196      * it will return when the indexing is done.
    197      */
    198     public void index(boolean waitForFinished) {
    199         log.info("Starting indexing.");
    200         /*
    201          * Synchronize here to make sure that you never get more than one
    202          * indexing thread trying to start at the same time.
    203          */
    204         synchronized (this) {
    205             if (indexThread != null) {
    206                 if (indexThread.isAlive()) {
    207                     log.warning("Previous indexer is still alive, will not start another.");
    208                     return;
    209                 } else {
    210                     log.fine("Previous indexer is no longer alive, starting a new one.");
    211                 }
    212             }
    213             indexThread = new Thread(this);
    214             try {
    215                 indexThread.start();
    216                 if (!waitForFinished) {
    217                     return;
    218                 }
    219                 log.fine("Waiting for indexer to finish...");
    220                 indexThread.join();
    221                 log.fine("indexer finished.");
    222             } catch (Exception e) {
    223                 log.log(Level.SEVERE,
    224                         "Caught Exception while waiting for indexing to finish.", e);
    225             }
    226             return;
    227         }
    228     }
    229 
    230     public void fileAdded(String path, String analyzer) {
    231         log.info("Added " + path + " analyzer " + analyzer);
    232         addFileAction("A:", path);
    233     }
    234 
    235     public void fileRemoved(String path) {
    236         log.info("File removed " + path);
    237         addFileAction("R:", path);
    238     }
    239 
    240     public void fileUpdated(String path) {
    241         log.info("File updated " + path);
    242         addFileAction("U:", path);
    243     }
    244 
    245     private void addFileAction(String type, String path) {
    246         notifications.append('\n');
    247         notifications.append(type);
    248         notifications.append(path);
    249         if (notifications.length() > MAXMESSAGELENGTH) {
    250             sendNotifications();
    251         }
    252     }
    253 
    254     private void sendNotifications() {
    255         if (notifications.length() > 0) {
    256             doNotify(NOTIFICATIONACTIONTYPE, "FilesInfo", notifications.toString());
    257             notifications.delete(0, notifications.length());
    258         }
    259     }
    260 
    261     public long lastIndexTimeFinished() {
    262         return lastIndexFinish;
    263     }
    264 
    265     public long lastIndexTimeStarted() {
    266         return lastIndexStart;
    267     }
    268 
    269     public long lastIndexTimeUsed() {
    270         return lastIndexUsedTime;
    271     }
    272 
    273     public Exception getExceptions() {
    274         return lastException;
    275     }
    276 
    277     public void addNotificationListener(NotificationListener notiflistener, NotificationFilter notfilt, Object obj) throws IllegalArgumentException {
    278         log.info("Adds a notiflistner, with obj " + obj.toString());
    279         if (notiflistener == null) {
    280             throw new IllegalArgumentException("Must have legal NotificationListener");
    281         }
    282         synchronized (notifListeners) {
    283             notifListeners.add(new NotificationHolder(notiflistener, notfilt, obj));
    284         }
    285     }
    286 
    287     public void removeNotificationListener(NotificationListener notiflistener) throws ListenerNotFoundException {
    288         log.info("removes a notiflistener, no obj");
    289         boolean removed = false;
    290         synchronized (notifListeners) {
    291             Iterator it = notifListeners.iterator();
    292             while (it.hasNext()) {
    293                 NotificationHolder mnf = (NotificationHolder) it.next();
    294                 if (mnf.getNL().equals(notiflistener)) {
    295                     it.remove();
    296                     removed = true;
    297                 }
    298             }
    299         }
    300         if (!removed) {
    301             throw new ListenerNotFoundException("Didn't remove the given NotificationListener");
    302         }
    303     }
    304 
    305     public void removeNotificationListener(NotificationListener notiflistener, NotificationFilter filt, Object obj) throws ListenerNotFoundException {
    306         log.info("removes a notiflistener obj " + obj);
    307         boolean removed = false;
    308         synchronized (notifListeners) {
    309             Iterator it = notifListeners.iterator();
    310             while (it.hasNext()) {
    311                 NotificationHolder mnf = (NotificationHolder) it.next();
    312                 if (mnf.getNL().equals(notiflistener)
    313                        && ((mnf.getFilter() == null) || mnf.getFilter().equals(filt))
    314                        && ((mnf.getFilter() == null) || mnf.getObj().equals(obj))) {
    315                             it.remove();
    316                             removed = true;
    317                 }
    318             }
    319         }
    320         if (!removed) {
    321             throw new ListenerNotFoundException("Didn't remove the given NotificationListener");
    322         }
    323     }
    324 
    325     /**
    326      * Method that the subclass can override, but doesn't have to
    327      * @return MBeanNotificationInfo array of notification (and types) this class can emitt.
    328      */
    329     public MBeanNotificationInfo[] getNotificationInfo() {
    330         MBeanNotificationInfo[] info = new MBeanNotificationInfo[1];
    331         String[] supptypes = {NOTIFICATIONACTIONTYPE, NOTIFICATIONINFOLONGTYPE, NOTIFICATIONINFOSTRINGTYPE};
    332         String name = "AgentIndexRunner";
    333         String descr = "OpenGrok Indexer Notifications";
    334         MBeanNotificationInfo minfo = new MBeanNotificationInfo(supptypes, name,
    335                 descr);
    336         info[0] = minfo;
    337         return info;
    338     }
    339 
    340     private void doNotify(String type, String msg, Object userdata) {
    341         try {
    342             log.info("start notifying " + notifListeners.size() + " listeners");
    343             long ts = System.currentTimeMillis();
    344             sequenceNo++;
    345             Notification notif = new Notification(type, this, sequenceNo, ts, msg);
    346             notif.setUserData(userdata);
    347             synchronized (notifListeners) {
    348                 for (NotificationHolder nl : notifListeners) {
    349                     log.fine("having one with obj " + nl.getObj());
    350                     try {
    351                         if ((nl.getFilter() == null) ||
    352                                 nl.getFilter().isNotificationEnabled(notif)) {
    353                             nl.getNL().handleNotification(notif, nl.getObj());
    354                         }
    355                     } catch (Exception exnot) {
    356                         log.log(Level.INFO, "Ex " + exnot, exnot);
    357                     }
    358                 }
    359             }
    360         } catch (Exception ex) {
    361             log.log(Level.SEVERE,
    362                     "Exception during notification sending: " + ex.getMessage(),
    363                     ex);
    364         }
    365     }
    366 }
    367