AppDaemonService.java

  1. /*
  2.  * Copyright (c) 2002-2022, City of Paris
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  *
  9.  *  1. Redistributions of source code must retain the above copyright notice
  10.  *     and the following disclaimer.
  11.  *
  12.  *  2. Redistributions in binary form must reproduce the above copyright notice
  13.  *     and the following disclaimer in the documentation and/or other materials
  14.  *     provided with the distribution.
  15.  *
  16.  *  3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
  17.  *     contributors may be used to endorse or promote products derived from
  18.  *     this software without specific prior written permission.
  19.  *
  20.  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  21.  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  22.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  23.  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
  24.  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  25.  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  26.  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  27.  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  28.  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  29.  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  30.  * POSSIBILITY OF SUCH DAMAGE.
  31.  *
  32.  * License 1.0
  33.  */
  34. package fr.paris.lutece.portal.service.daemon;

  35. import java.util.Collection;
  36. import java.util.HashMap;
  37. import java.util.Map;
  38. import java.util.Random;
  39. import java.util.concurrent.TimeUnit;

  40. import fr.paris.lutece.portal.service.datastore.DatastoreService;
  41. import fr.paris.lutece.portal.service.init.LuteceInitException;
  42. import fr.paris.lutece.portal.service.spring.SpringContextService;
  43. import fr.paris.lutece.portal.service.util.AppLogService;
  44. import fr.paris.lutece.portal.service.util.AppPropertiesService;

  45. /**
  46.  * this class provides methods to manage daemons services
  47.  */
  48. public final class AppDaemonService
  49. {
  50.     private static final String PROPERTY_MAX_INITIAL_START_DELAY = "daemon.maxInitialStartDelay";
  51.     private static final String PROPERTY_DAEMON_ON_STARTUP = ".onStartUp";
  52.     private static final String PROPERTY_DAEMON_INTERVAL = ".interval";
  53.     private static final String KEY_DAEMON = "daemon.";
  54.     private static final String KEY_DAEMON_PREFIX = "core." + KEY_DAEMON;
  55.     private static final Map<String, DaemonEntry> _mapDaemonEntries = new HashMap<>( );
  56.     private static final Random _random = new Random( );
  57.     private static boolean _bInit;
  58.     private static IDaemonScheduler _executor;

  59.     /** Private constructor */
  60.     private AppDaemonService( )
  61.     {
  62.     }

  63.     /**
  64.      * Performs initialization of the DaemonFactory. Note that this should return right away so that processing can continue (IE thread off everything)
  65.      *
  66.      * @throws LuteceInitException
  67.      *             If an error occurred
  68.      */
  69.     public static synchronized void init( )
  70.     {
  71.         // already initialized
  72.         if ( _bInit )
  73.         {
  74.             return;
  75.         }

  76.         _executor = SpringContextService.getBean( IDaemonScheduler.BEAN_NAME );

  77.         if ( _mapDaemonEntries.size( ) > 0 )
  78.         {
  79.             // Unsynchronized daemon start
  80.             int nInitialDaemon = 0;

  81.             for ( DaemonEntry entry : _mapDaemonEntries.values( ) )
  82.             {
  83.                 if ( entry.onStartup( ) )
  84.                 {
  85.                     nInitialDaemon++;
  86.                 }
  87.             }

  88.             int nDelay = AppPropertiesService.getPropertyInt( PROPERTY_MAX_INITIAL_START_DELAY, 30 );

  89.             if ( nInitialDaemon > 0 )
  90.             {
  91.                 nDelay = nDelay / nInitialDaemon;
  92.             }

  93.             int nInitialDelay = 0;

  94.             // Register daemons
  95.             for ( DaemonEntry entry : _mapDaemonEntries.values( ) )
  96.             {
  97.                 // starts any daemon declared as startup daemons
  98.                 if ( entry.onStartup( ) )
  99.                 {
  100.                     nInitialDelay += nDelay;

  101.                     scheduleThread( entry, nInitialDelay );
  102.                 }
  103.             }
  104.         }

  105.         _bInit = true;
  106.     }

  107.     /**
  108.      * Register a daemon by its entry
  109.      *
  110.      * @param entry
  111.      *            The daemon entry
  112.      * @throws LuteceInitException
  113.      *             If an error occurred
  114.      */
  115.     public static void registerDaemon( DaemonEntry entry ) throws LuteceInitException
  116.     {
  117.         if ( _mapDaemonEntries.containsKey( entry.getId( ) ) )
  118.         {
  119.             AppLogService.error( "Ignoring attempt to register already registered daemon {}", entry.getId( ) );
  120.             return;
  121.         }
  122.         String strIntervalKey = getIntervalKey( entry.getId( ) );
  123.         String strIntervalKeyDefaultValue = null;

  124.         // init interval value if no exists
  125.         if ( !DatastoreService.existsInstanceKey( strIntervalKey ) )
  126.         {
  127.             strIntervalKeyDefaultValue = AppPropertiesService.getProperty( KEY_DAEMON + entry.getId( ) + PROPERTY_DAEMON_INTERVAL, "10" );
  128.             DatastoreService.setInstanceDataValue( strIntervalKey, strIntervalKeyDefaultValue );
  129.         }

  130.         String strIntervalKeyValue = DatastoreService.getInstanceDataValue( strIntervalKey, strIntervalKeyDefaultValue );

  131.         long lInterval = Long.parseLong( strIntervalKeyValue );

  132.         String strOnStartupKey = getOnStartupKey( entry.getId( ) );
  133.         String strOnStartupDefaultValue = null;

  134.         // init onStartup value if no exists
  135.         if ( !DatastoreService.existsInstanceKey( strOnStartupKey ) )
  136.         {
  137.             strOnStartupDefaultValue = AppPropertiesService.getProperty( KEY_DAEMON + entry.getId( ) + ".onstartup", "0" ).equals( "1" )
  138.                     ? DatastoreService.VALUE_TRUE
  139.                     : DatastoreService.VALUE_FALSE;
  140.             DatastoreService.setInstanceDataValue( strOnStartupKey, strOnStartupDefaultValue );
  141.         }

  142.         String strOnStarupvalue = DatastoreService.getInstanceDataValue( strOnStartupKey, strOnStartupDefaultValue );
  143.         boolean bOnStartup = Boolean.parseBoolean( strOnStarupvalue );

  144.         entry.setInterval( lInterval );
  145.         entry.setOnStartUp( bOnStartup );

  146.         try
  147.         {
  148.             entry.loadDaemon( );
  149.         }
  150.         catch( IllegalAccessException | InstantiationException | ClassNotFoundException e )
  151.         {
  152.             throw new LuteceInitException( "Couldn't instantiate daemon: " + entry.getId( ), e );
  153.         }

  154.         // Add plugin name to Daemon class
  155.         if ( entry.getPluginName( ) != null )
  156.         {
  157.             entry.getDaemon( ).setPluginName( entry.getPluginName( ) );
  158.         }

  159.         _mapDaemonEntries.put( entry.getId( ), entry );

  160.         AppLogService.info( "New Daemon registered : {} ", entry.getId( ) );
  161.     }

  162.     /**
  163.      * Unregister a daemon
  164.      *
  165.      * @param strDaemonKey
  166.      *            The daemon key
  167.      */
  168.     public static void unregisterDaemon( String strDaemonKey )
  169.     {
  170.         unScheduleThread( _mapDaemonEntries.get( strDaemonKey ) );
  171.         _mapDaemonEntries.remove( strDaemonKey );
  172.     }

  173.     /**
  174.      * Starts a daemon
  175.      *
  176.      * @param strDaemonKey
  177.      *            The daemon key
  178.      */
  179.     public static void startDaemon( String strDaemonKey )
  180.     {
  181.         scheduleThread( _mapDaemonEntries.get( strDaemonKey ) );
  182.     }

  183.     /**
  184.      * Stops a daemon
  185.      *
  186.      * @param strDaemonKey
  187.      *            The daemon key
  188.      */
  189.     public static void stopDaemon( String strDaemonKey )
  190.     {
  191.         unScheduleThread( _mapDaemonEntries.get( strDaemonKey ) );
  192.     }

  193.     /**
  194.      * Signal a daemon for execution in the immediate future.
  195.      * <p>
  196.      * This can fail is resources are limited, which should be exceptional.
  197.      *
  198.      * @param strDaemonKey
  199.      *            the daemon key
  200.      * @return <code>true</code> if the daemon was successfully signaled, <code>false</code> otherwise
  201.      * @since 6.0.0
  202.      */
  203.     public static boolean signalDaemon( String strDaemonKey )
  204.     {
  205.         return signalDaemon( strDaemonKey, 0L, TimeUnit.MILLISECONDS );
  206.     }

  207.     /**
  208.      * Signal a daemon for execution in the immediate future.
  209.      * <p>
  210.      * This can fail is resources are limited, which should be exceptional.
  211.      *
  212.      * @param strDaemonKey
  213.      *            the daemon key
  214.      * @param nDelay
  215.      *            the delay before execution
  216.      * @param unit
  217.      *            the unit of <code>nDelay</code> argument
  218.      * @return <code>true</code> if the daemon was successfully signaled, <code>false</code> otherwise
  219.      * @since 6.0.0
  220.      */
  221.     public static boolean signalDaemon( String strDaemonKey, long nDelay, TimeUnit unit )
  222.     {
  223.         return _executor.enqueue( _mapDaemonEntries.get( strDaemonKey ), nDelay, unit );
  224.     }

  225.     /**
  226.      * modify daemon interval
  227.      *
  228.      * @param strDaemonKey
  229.      *            The daemon key
  230.      * @param strDaemonInterval
  231.      *            the daemon interval
  232.      */
  233.     public static void modifyDaemonInterval( String strDaemonKey, String strDaemonInterval )
  234.     {
  235.         DaemonEntry entry = _mapDaemonEntries.get( strDaemonKey );

  236.         if ( entry != null )
  237.         {
  238.             entry.setInterval( Long.valueOf( strDaemonInterval ) );
  239.             DatastoreService.setInstanceDataValue( getIntervalKey( entry.getId( ) ), strDaemonInterval );
  240.         }
  241.     }

  242.     /**
  243.      * Add daemon to schedule's queue
  244.      *
  245.      * @param entry
  246.      *            The DaemonEntry
  247.      */
  248.     private static void scheduleThread( DaemonEntry entry )
  249.     {
  250.         scheduleThread( entry, _random.nextInt( AppPropertiesService.getPropertyInt( PROPERTY_MAX_INITIAL_START_DELAY, 30 ) ) );
  251.     }

  252.     /**
  253.      * Add daemon to schedule's queue
  254.      *
  255.      * @param entry
  256.      *            The DaemonEntry
  257.      * @param nInitialDelay
  258.      *            Initial start delay
  259.      */
  260.     private static void scheduleThread( DaemonEntry entry, int nInitialDelay )
  261.     {
  262.         AppLogService.info( "Scheduling daemon {} ; first run in {} seconds", entry.getId( ), nInitialDelay );
  263.         entry.setIsRunning( true );
  264.         _executor.schedule( entry, nInitialDelay, TimeUnit.SECONDS );
  265.         // update onStartup property
  266.         DatastoreService.setInstanceDataValue( getOnStartupKey( entry.getId( ) ), DatastoreService.VALUE_TRUE );
  267.     }

  268.     /**
  269.      * Remove daemon from schedule's queue
  270.      *
  271.      * @param entry
  272.      *            The DaemonEntry
  273.      */
  274.     private static void unScheduleThread( DaemonEntry entry )
  275.     {
  276.         cancelScheduledThread( entry.getId( ) );
  277.         entry.setIsRunning( false );
  278.         // update onStartup property
  279.         DatastoreService.setInstanceDataValue( getOnStartupKey( entry.getId( ) ), DatastoreService.VALUE_FALSE );
  280.         AppLogService.info( "Stopping daemon '{}'", entry.getId( ) );
  281.     }

  282.     /**
  283.      * Cancel scheduled thread (don't interrupt if it is running )
  284.      *
  285.      * @param strEntryId
  286.      *            The DaemonEntry Id
  287.      */
  288.     protected static void cancelScheduledThread( String strEntryId )
  289.     {
  290.         _executor.unSchedule( _mapDaemonEntries.get( strEntryId ) );
  291.     }

  292.     /**
  293.      * Get the current known DaemonEntries within the DaemonFactory
  294.      *
  295.      * @return the entries list of daemons declaration
  296.      */
  297.     public static Collection<DaemonEntry> getDaemonEntries( )
  298.     {
  299.         return _mapDaemonEntries.values( );
  300.     }

  301.     /**
  302.      * Performs the shutdown of the DaemonFactory.
  303.      */
  304.     public static void shutdown( )
  305.     {
  306.         _executor.shutdown( );
  307.     }

  308.     /**
  309.      * Gets a daemon object from its key name
  310.      *
  311.      * @param strDaemonKey
  312.      *            The daemon key
  313.      * @return The daemon
  314.      */
  315.     public static Daemon getDaemon( String strDaemonKey )
  316.     {
  317.         DaemonEntry entry = _mapDaemonEntries.get( strDaemonKey );

  318.         return entry.getDaemon( );
  319.     }

  320.     /**
  321.      * return the OnStartup key link to the daemon
  322.      *
  323.      * @param strDaemonKey
  324.      *            The daemon key
  325.      * @return The key
  326.      */
  327.     private static String getOnStartupKey( String strDaemonKey )
  328.     {
  329.         return KEY_DAEMON_PREFIX + strDaemonKey + PROPERTY_DAEMON_ON_STARTUP;
  330.     }

  331.     /**
  332.      * return the Interval key link to the daemon
  333.      *
  334.      * @param strDaemonKey
  335.      *            The daemon key
  336.      * @return The key
  337.      */
  338.     private static String getIntervalKey( String strDaemonKey )
  339.     {
  340.         return KEY_DAEMON_PREFIX + strDaemonKey + PROPERTY_DAEMON_INTERVAL;
  341.     }
  342. }