Skip to content

Commit

Permalink
SmartLifecycle default methods for auto-startup in default phase
Browse files Browse the repository at this point in the history
Issue: SPR-17188
  • Loading branch information
jhoeller committed Aug 16, 2018
1 parent 446a604 commit 2ec8fa9
Show file tree
Hide file tree
Showing 15 changed files with 97 additions and 144 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ public static DataSource getConfigTimeNonTransactionalDataSource() {

private int startupDelay = 0;

private int phase = Integer.MAX_VALUE;
private int phase = DEFAULT_PHASE;

private boolean exposeSchedulerInRepository = false;

Expand Down Expand Up @@ -796,12 +796,6 @@ public void stop() throws SchedulingException {
}
}

@Override
public void stop(Runnable callback) throws SchedulingException {
stop();
callback.run();
}

@Override
public boolean isRunning() throws SchedulingException {
if (this.scheduler != null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -34,10 +34,11 @@
* restricting the visibility of activity-controlled components to the Lifecycle
* interface.
*
* <p>Note that the Lifecycle interface is only supported on <b>top-level singleton
* beans</b>. On any other component, the Lifecycle interface will remain undetected
* and hence ignored. Also, note that the extended {@link SmartLifecycle} interface
* provides integration with the application context's startup and shutdown phases.
* <p>Note that the present {@code Lifecycle} interface is only supported on
* <b>top-level singleton beans</b>. On any other component, the {@code Lifecycle}
* interface will remain undetected and hence ignored. Also, note that the extended
* {@link SmartLifecycle} interface provides sophisticated integration with the
* application context's startup and shutdown phases.
*
* @author Juergen Hoeller
* @since 2.0
Expand All @@ -61,11 +62,12 @@ public interface Lifecycle {
* Stop this component, typically in a synchronous fashion, such that the component is
* fully stopped upon return of this method. Consider implementing {@link SmartLifecycle}
* and its {@code stop(Runnable)} variant when asynchronous stop behavior is necessary.
* <p>Note that this stop notification is not guaranteed to come before destruction: On
* regular shutdown, {@code Lifecycle} beans will first receive a stop notification before
* the general destruction callbacks are being propagated; however, on hot refresh during a
* context's lifetime or on aborted refresh attempts, only destroy methods will be called.
* <p>Should not throw an exception if the component isn't started yet.
* <p>Note that this stop notification is not guaranteed to come before destruction:
* On regular shutdown, {@code Lifecycle} beans will first receive a stop notification
* before the general destruction callbacks are being propagated; however, on hot
* refresh during a context's lifetime or on aborted refresh attempts, a given bean's
* destroy method will be called without any consideration of stop signals upfront.
* <p>Should not throw an exception if the component is not running (not started yet).
* <p>In the case of a container, this will propagate the stop signal to all components
* that apply.
* @see SmartLifecycle#stop(Runnable)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -23,56 +23,72 @@
* be started at the time of a context refresh. The callback-accepting
* {@link #stop(Runnable)} method is useful for objects that have an asynchronous
* shutdown process. Any implementation of this interface <i>must</i> invoke the
* callback's run() method upon shutdown completion to avoid unnecessary delays
* in the overall ApplicationContext shutdown.
* callback's {@code run()} method upon shutdown completion to avoid unnecessary
* delays in the overall ApplicationContext shutdown.
*
* <p>This interface extends {@link Phased}, and the {@link #getPhase()} method's
* return value indicates the phase within which this Lifecycle component should
* be started and stopped. The startup process begins with the <i>lowest</i>
* phase value and ends with the <i>highest</i> phase value (Integer.MIN_VALUE
* is the lowest possible, and Integer.MAX_VALUE is the highest possible). The
* shutdown process will apply the reverse order. Any components with the
* be started and stopped. The startup process begins with the <i>lowest</i> phase
* value and ends with the <i>highest</i> phase value ({@code Integer.MIN_VALUE}
* is the lowest possible, and {@code Integer.MAX_VALUE} is the highest possible).
* The shutdown process will apply the reverse order. Any components with the
* same value will be arbitrarily ordered within the same phase.
*
* <p>Example: if component B depends on component A having already started, then
* component A should have a lower phase value than component B. During the
* shutdown process, component B would be stopped before component A.
* <p>Example: if component B depends on component A having already started,
* then component A should have a lower phase value than component B. During
* the shutdown process, component B would be stopped before component A.
*
* <p>Any explicit "depends-on" relationship will take precedence over
* the phase order such that the dependent bean always starts after its
* dependency and always stops before its dependency.
* <p>Any explicit "depends-on" relationship will take precedence over the phase
* order such that the dependent bean always starts after its dependency and
* always stops before its dependency.
*
* <p>Any Lifecycle components within the context that do not also implement
* SmartLifecycle will be treated as if they have a phase value of 0. That
* way a SmartLifecycle implementation may start before those Lifecycle
* components if it has a negative phase value, or it may start after
* those components if it has a positive phase value.
* <p>Any {@code Lifecycle} components within the context that do not also
* implement {@code SmartLifecycle} will be treated as if they have a phase
* value of 0. That way a {@code SmartLifecycle} implementation may start
* before those {@code Lifecycle} components if it has a negative phase value,
* or it may start after those components if it has a positive phase value.
*
* <p>Note that, due to the auto-startup support in SmartLifecycle,
* a SmartLifecycle bean instance will get initialized on startup of the
* application context in any case. As a consequence, the bean definition
* lazy-init flag has very limited actual effect on SmartLifecycle beans.
* <p>Note that, due to the auto-startup support in {@code SmartLifecycle}, a
* {@code SmartLifecycle} bean instance will usually get initialized on startup
* of the application context in any case. As a consequence, the bean definition
* lazy-init flag has very limited actual effect on {@code SmartLifecycle} beans.
*
* @author Mark Fisher
* @author Juergen Hoeller
* @since 3.0
* @see LifecycleProcessor
* @see ConfigurableApplicationContext
*/
public interface SmartLifecycle extends Lifecycle, Phased {

/**
* The default phase for {@code SmartLifecycle}: {@code Integer.MAX_VALUE}.
* <p>This is different from the common phase 0 associated with regular
* {@link Lifecycle} implementations, putting the typically auto-started
* {@code SmartLifecycle} beans into a separate later shutdown phase.
* @since 5.1
* @see #getPhase()
* @see org.springframework.context.support.DefaultLifecycleProcessor#getPhase(Lifecycle)
*/
int DEFAULT_PHASE = Integer.MAX_VALUE;


/**
* Returns {@code true} if this {@code Lifecycle} component should get
* started automatically by the container at the time that the containing
* {@link ApplicationContext} gets refreshed.
* <p>A value of {@code false} indicates that the component is intended to
* be started through an explicit {@link #start()} call instead, analogous
* to a plain {@link Lifecycle} implementation.
* <p>The default implementation returns {@code true}.
* @see #start()
* @see #getPhase()
* @see LifecycleProcessor#onRefresh()
* @see ConfigurableApplicationContext#refresh()
*/
boolean isAutoStartup();
default boolean isAutoStartup() {
return true;
}

/**
* Indicates that a Lifecycle component must stop if it is currently running.
Expand All @@ -84,9 +100,30 @@ public interface SmartLifecycle extends Lifecycle, Phased {
* {@code stop} method; i.e. {@link Lifecycle#stop()} will not be called for
* {@code SmartLifecycle} implementations unless explicitly delegated to within
* the implementation of this method.
* <p>The default implementation delegates to {@link #stop()} and immediately
* triggers the given callback in the calling thread. Note that there is no
* synchronization between the two, so custom implementations may at least
* want to put the same steps within their common lifecycle monitor (if any).
* @see #stop()
* @see #getPhase()
*/
void stop(Runnable callback);
default void stop(Runnable callback) {
stop();
callback.run();
}

/**
* Return the phase that this lifecycle object is supposed to run in.
* <p>The default implementation returns {@link #DEFAULT_PHASE} in order to
* let stop callbacks execute after regular {@code Lifecycle} implementations.
* @see #isAutoStartup()
* @see #start()
* @see #stop(Runnable)
* @see org.springframework.context.support.DefaultLifecycleProcessor#getPhase(Lifecycle)
*/
@Override
default int getPhase() {
return DEFAULT_PHASE;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -195,11 +195,11 @@ private void stopBeans() {
Map<String, Lifecycle> lifecycleBeans = getLifecycleBeans();
Map<Integer, LifecycleGroup> phases = new HashMap<>();
lifecycleBeans.forEach((beanName, bean) -> {
int shutdownOrder = getPhase(bean);
LifecycleGroup group = phases.get(shutdownOrder);
int shutdownPhase = getPhase(bean);
LifecycleGroup group = phases.get(shutdownPhase);
if (group == null) {
group = new LifecycleGroup(shutdownOrder, this.timeoutPerShutdownPhase, lifecycleBeans, false);
phases.put(shutdownOrder, group);
group = new LifecycleGroup(shutdownPhase, this.timeoutPerShutdownPhase, lifecycleBeans, false);
phases.put(shutdownPhase, group);
}
group.add(beanName, bean);
});
Expand Down Expand Up @@ -302,11 +302,11 @@ private boolean matchesBeanType(Class<?> targetType, String beanName, BeanFactor

/**
* Determine the lifecycle phase of the given bean.
* <p>The default implementation checks for the {@link Phased} interface.
* Can be overridden to apply other/further policies.
* <p>The default implementation checks for the {@link Phased} interface, using
* a default of 0 otherwise. Can be overridden to apply other/further policies.
* @param bean the bean to introspect
* @return the phase an integer value. The suggested default is 0.
* @see Phased
* @return the phase (an integer value)
* @see Phased#getPhase()
* @see SmartLifecycle
*/
protected int getPhase(Lifecycle bean) {
Expand Down Expand Up @@ -412,9 +412,9 @@ private class LifecycleGroupMember implements Comparable<LifecycleGroupMember> {

@Override
public int compareTo(LifecycleGroupMember other) {
int thisOrder = getPhase(this.bean);
int otherOrder = getPhase(other.bean);
return Integer.compare(thisOrder, otherOrder);
int thisPhase = getPhase(this.bean);
int otherPhase = getPhase(other.bean);
return Integer.compare(thisPhase, otherPhase);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public class JmsListenerEndpointRegistry implements DisposableBean, SmartLifecyc
private final Map<String, MessageListenerContainer> listenerContainers =
new ConcurrentHashMap<>();

private int phase = Integer.MAX_VALUE;
private int phase = DEFAULT_PHASE;

@Nullable
private ApplicationContext applicationContext;
Expand Down Expand Up @@ -198,11 +198,6 @@ public int getPhase() {
return this.phase;
}

@Override
public boolean isAutoStartup() {
return true;
}

@Override
public void start() {
for (MessageListenerContainer listenerContainer : getListenerContainers()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public abstract class AbstractJmsListeningContainer extends JmsDestinationAccess

private boolean autoStartup = true;

private int phase = Integer.MAX_VALUE;
private int phase = DEFAULT_PHASE;

@Nullable
private String beanName;
Expand Down Expand Up @@ -319,12 +319,6 @@ public void stop() throws JmsException {
}
}

@Override
public void stop(Runnable callback) {
stop();
callback.run();
}

/**
* Notify all invoker tasks and stop the shared Connection, if any.
* @throws JMSException if thrown by JMS API methods
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -268,16 +268,6 @@ public MessageHeaderInitializer getHeaderInitializer() {
}


@Override
public boolean isAutoStartup() {
return true;
}

@Override
public int getPhase() {
return Integer.MAX_VALUE;
}

@Override
public final void start() {
synchronized (this.lifecycleMonitor) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,11 +173,6 @@ public boolean isAutoStartup() {
return this.autoStartup;
}

@Override
public int getPhase() {
return Integer.MAX_VALUE;
}


@Override
public void start() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,16 +147,6 @@ public MessageHeaderInitializer getHeaderInitializer() {
}


@Override
public int getPhase() {
return Integer.MAX_VALUE;
}

@Override
public boolean isAutoStartup() {
return true;
}

@Override
public final void start() {
synchronized (this.lifecycleMonitor) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ public class GenericMessageEndpointManager implements SmartLifecycle, Initializi

private boolean autoStartup = true;

private int phase = Integer.MAX_VALUE;
private int phase = DEFAULT_PHASE;

private volatile boolean running = false;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,21 +71,6 @@ public void setBufferFactory(DataBufferFactory bufferFactory) {
}


@Override
public int getPhase() {
return Integer.MAX_VALUE;
}

@Override
public boolean isAutoStartup() {
return true;
}

@Override
public boolean isRunning() {
return this.httpClient.isRunning();
}

@Override
public void start() {
try {
Expand All @@ -108,9 +93,8 @@ public void stop() {
}

@Override
public void stop(Runnable callback) {
stop();
callback.run();
public boolean isRunning() {
return this.httpClient.isRunning();
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public abstract class ConnectionManagerSupport implements SmartLifecycle {

private boolean autoStartup = false;

private int phase = Integer.MAX_VALUE;
private int phase = DEFAULT_PHASE;

private volatile boolean running = false;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -254,16 +254,6 @@ public String getStatsInfo() {
}


@Override
public boolean isAutoStartup() {
return true;
}

@Override
public int getPhase() {
return Integer.MAX_VALUE;
}

@Override
public final void start() {
Assert.isTrue(this.defaultProtocolHandler != null || !this.protocolHandlers.isEmpty(), "No handlers");
Expand Down
Loading

0 comments on commit 2ec8fa9

Please sign in to comment.