PwmServiceManager.java 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. /*
  2. * Password Management Servlets (PWM)
  3. * http://www.pwm-project.org
  4. *
  5. * Copyright (c) 2006-2009 Novell, Inc.
  6. * Copyright (c) 2009-2021 The PWM Project
  7. *
  8. * Licensed under the Apache License, Version 2.0 (the "License");
  9. * you may not use this file except in compliance with the License.
  10. * You may obtain a copy of the License at
  11. *
  12. * http://www.apache.org/licenses/LICENSE-2.0
  13. *
  14. * Unless required by applicable law or agreed to in writing, software
  15. * distributed under the License is distributed on an "AS IS" BASIS,
  16. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17. * See the License for the specific language governing permissions and
  18. * limitations under the License.
  19. */
  20. package password.pwm.svc;
  21. import password.pwm.PwmApplication;
  22. import password.pwm.PwmEnvironment;
  23. import password.pwm.bean.DomainID;
  24. import password.pwm.bean.SessionLabel;
  25. import password.pwm.error.ErrorInformation;
  26. import password.pwm.error.PwmError;
  27. import password.pwm.error.PwmException;
  28. import password.pwm.error.PwmUnrecoverableException;
  29. import password.pwm.util.java.StatisticCounterBundle;
  30. import password.pwm.util.java.TimeDuration;
  31. import password.pwm.util.logging.PwmLogger;
  32. import java.time.Instant;
  33. import java.util.ArrayList;
  34. import java.util.Collections;
  35. import java.util.List;
  36. import java.util.Map;
  37. import java.util.Objects;
  38. import java.util.concurrent.ConcurrentHashMap;
  39. public class PwmServiceManager
  40. {
  41. private static final PwmLogger LOGGER = PwmLogger.forClass( PwmServiceManager.class );
  42. private final DomainID domainID;
  43. private final PwmApplication pwmApplication;
  44. private final Map<PwmServiceEnum, PwmService> runningServices = new ConcurrentHashMap<>();
  45. private final List<PwmServiceEnum> availableServices;
  46. private final SessionLabel sessionLabel;
  47. private boolean initialized;
  48. public PwmServiceManager(
  49. final SessionLabel sessionLabel,
  50. final PwmApplication pwmApplication,
  51. final DomainID domainID,
  52. final List<PwmServiceEnum> services
  53. )
  54. {
  55. this.sessionLabel = sessionLabel;
  56. this.domainID = Objects.requireNonNull( domainID );
  57. this.availableServices = List.copyOf( services );
  58. this.pwmApplication = Objects.requireNonNull( pwmApplication );
  59. }
  60. private enum InitializationStats
  61. {
  62. starts,
  63. stops,
  64. restarts,
  65. }
  66. public PwmService getService( final PwmServiceEnum serviceClass )
  67. {
  68. return runningServices.get( serviceClass );
  69. }
  70. public void initAllServices()
  71. throws PwmUnrecoverableException
  72. {
  73. final Instant startTime = Instant.now();
  74. final boolean internalRuntimeInstance = pwmApplication.getPwmEnvironment().isInternalRuntimeInstance()
  75. || pwmApplication.getPwmEnvironment().getFlags().contains( PwmEnvironment.ApplicationFlag.CommandLineInstance );
  76. final String logVerb = initialized ? "restart" : "start";
  77. final StatisticCounterBundle<InitializationStats> statCounter = new StatisticCounterBundle<>( InitializationStats.class );
  78. LOGGER.trace( sessionLabel, () -> "beginning service " + logVerb + " process" );
  79. for ( final PwmServiceEnum serviceClassEnum : availableServices )
  80. {
  81. boolean serviceShouldBeRunning = true;
  82. if ( internalRuntimeInstance && !serviceClassEnum.isInternalRuntime() )
  83. {
  84. serviceShouldBeRunning = false;
  85. }
  86. if ( serviceShouldBeRunning )
  87. {
  88. if ( runningServices.containsKey( serviceClassEnum ) )
  89. {
  90. shutDownService( runningServices.get( serviceClassEnum ) );
  91. statCounter.increment( InitializationStats.restarts );
  92. }
  93. else
  94. {
  95. statCounter.increment( InitializationStats.starts );
  96. }
  97. final PwmService newServiceInstance = initService( serviceClassEnum );
  98. runningServices.put( serviceClassEnum, newServiceInstance );
  99. }
  100. else
  101. {
  102. if ( runningServices.containsKey( serviceClassEnum ) )
  103. {
  104. shutDownService( runningServices.get( serviceClassEnum ) );
  105. statCounter.increment( InitializationStats.stops );
  106. }
  107. }
  108. }
  109. initialized = true;
  110. LOGGER.trace( sessionLabel, () -> logVerb + "ed services, " + statCounter.debugStats(), TimeDuration.fromCurrent( startTime ) );
  111. }
  112. private String debugSvcType()
  113. {
  114. return ( domainID.isSystem() ? "system" : "domain" ) + " service";
  115. }
  116. private PwmService initService( final PwmServiceEnum pwmServiceEnum )
  117. throws PwmUnrecoverableException
  118. {
  119. final Instant startTime = Instant.now();
  120. final PwmService newServiceInstance;
  121. try
  122. {
  123. final Class<? extends PwmService> serviceClass = pwmServiceEnum.getPwmServiceClass();
  124. newServiceInstance = serviceClass.getDeclaredConstructor().newInstance();
  125. }
  126. catch ( final Exception e )
  127. {
  128. final String errorMsg = "unexpected error instantiating " + debugSvcType() + " class '" + pwmServiceEnum.name() + "', error: " + e;
  129. LOGGER.fatal( () -> errorMsg, e );
  130. throw new PwmUnrecoverableException( new ErrorInformation( PwmError.ERROR_STARTUP_ERROR, errorMsg ) );
  131. }
  132. try
  133. {
  134. LOGGER.trace( sessionLabel, () -> "initializing service " + newServiceInstance.name() );
  135. newServiceInstance.init( pwmApplication, domainID );
  136. final TimeDuration startupDuration = TimeDuration.fromCurrent( startTime );
  137. LOGGER.debug( sessionLabel, () -> "completed initialization of " + debugSvcType()
  138. + " " + newServiceInstance.name()
  139. + ", status=" + newServiceInstance.status(), startupDuration );
  140. }
  141. catch ( final PwmException e )
  142. {
  143. LOGGER.warn( sessionLabel, () -> "error instantiating " + debugSvcType() + " class '" + newServiceInstance.name()
  144. + "', service will remain unavailable, error: " + e.getMessage() );
  145. }
  146. catch ( final Exception e )
  147. {
  148. String errorMsg = "unexpected error instantiating " + debugSvcType() + " class '" + newServiceInstance.name() + "', cannot load, error: " + e.getMessage();
  149. if ( e.getCause() != null )
  150. {
  151. errorMsg += ", cause: " + e.getCause();
  152. }
  153. final String errorMsgFinal = errorMsg;
  154. LOGGER.fatal( sessionLabel, () -> errorMsgFinal );
  155. throw new PwmUnrecoverableException( new ErrorInformation( PwmError.ERROR_STARTUP_ERROR, errorMsg ) );
  156. }
  157. return newServiceInstance;
  158. }
  159. public void shutdownAllServices( )
  160. {
  161. if ( !initialized )
  162. {
  163. return;
  164. }
  165. final int serviceCount = availableServices.size();
  166. LOGGER.trace( sessionLabel, () -> "beginning to close " + serviceCount + " " + debugSvcType() + "s" );
  167. final Instant startTime = Instant.now();
  168. final List<PwmServiceEnum> reverseServiceList = new ArrayList<>( availableServices );
  169. Collections.reverse( reverseServiceList );
  170. for ( final PwmServiceEnum pwmServiceEnum : reverseServiceList )
  171. {
  172. if ( runningServices.containsKey( pwmServiceEnum ) )
  173. {
  174. shutDownService( runningServices.get( pwmServiceEnum ) );
  175. }
  176. }
  177. initialized = false;
  178. LOGGER.trace( sessionLabel, () -> "closed " + serviceCount + " " + debugSvcType() + "s", TimeDuration.fromCurrent( startTime ) );
  179. }
  180. private void shutDownService( final PwmService serviceInstance )
  181. {
  182. LOGGER.trace( sessionLabel, () -> "closing " + debugSvcType() + " " + serviceInstance.name() );
  183. try
  184. {
  185. final Instant startTime = Instant.now();
  186. serviceInstance.shutdown();
  187. final TimeDuration timeDuration = TimeDuration.fromCurrent( startTime );
  188. LOGGER.trace( sessionLabel, () -> "successfully closed " + debugSvcType() + " " + serviceInstance.name()
  189. + " (" + timeDuration.asCompactString() + ")" );
  190. }
  191. catch ( final Exception e )
  192. {
  193. LOGGER.error( sessionLabel, () -> "error closing " + debugSvcType() + " " + serviceInstance.name() + ": " + e.getMessage(), e );
  194. }
  195. }
  196. public List<PwmService> getRunningServices( )
  197. {
  198. return List.copyOf( this.runningServices.values() );
  199. }
  200. }