/* * Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com * The software in this package is published under the terms of the CPAL v1.0 * license, a copy of which has been included with this distribution in the * LICENSE.txt file. */ package org.mule.runtime.core.api.lifecycle; import static java.lang.String.format; import static org.mule.runtime.api.i18n.I18nMessageFactory.createStaticMessage; import static org.mule.runtime.api.util.Preconditions.checkArgument; import org.mule.runtime.api.exception.MuleException; import org.mule.runtime.api.exception.MuleRuntimeException; import org.mule.runtime.api.i18n.I18nMessage; import org.mule.runtime.api.lifecycle.Disposable; import org.mule.runtime.api.lifecycle.Initialisable; import org.mule.runtime.api.lifecycle.InitialisationException; import org.mule.runtime.api.lifecycle.Startable; import org.mule.runtime.api.lifecycle.Stoppable; import org.mule.runtime.core.api.MuleContext; import org.mule.runtime.core.api.construct.FlowConstruct; import org.mule.runtime.core.api.construct.FlowConstructAware; import org.mule.runtime.core.api.context.MuleContextAware; import java.util.Collection; import java.util.Optional; import org.apache.commons.collections.CollectionUtils; import org.slf4j.Logger; /** * Utility class for performing lifecycle operations on objects, as long as they implement cooresponding annotations such as * {@link Initialisable}, {@link Startable}, {@link Stoppable}, {@link Disposable} or even {@link MuleContextAware}. * <p> * The {@link Optional} object container is also supported, in which case the operation will be evaluated on the value it holds or * not at all if the value is not present. * * @since 3.7.0 */ public class LifecycleUtils { private LifecycleUtils() {} /** * Invokes {@link Initialisable#initialise()} on {@code object} if it implements the {@link Initialisable} interface. * * @param object the object you're trying to initialise * @throws InitialisationException */ public static void initialiseIfNeeded(Object object) throws InitialisationException { object = unwrap(object); if (object instanceof Initialisable) { ((Initialisable) object).initialise(); } } /** * The same as {@link #initialiseIfNeeded(Object, MuleContext)}, only that it also checks if it implements * {@link FlowConstructAware}, in which case it will invoke {@link FlowConstructAware#setFlowConstruct(FlowConstruct)} with the * given {@code flowConstruct} * * @param object the object you're trying to initialise * @param muleContext a {@link MuleContext} * @param flowConstruct the {@link FlowConstruct} in which the object is defined. * @throws InitialisationException */ public static void initialiseIfNeeded(Object object, MuleContext muleContext, FlowConstruct flowConstruct) throws InitialisationException { object = unwrap(object); if (flowConstruct != null && object instanceof FlowConstructAware) { ((FlowConstructAware) object).setFlowConstruct(flowConstruct); } initialiseIfNeeded(object, muleContext); } /** * The same as {@link #initialiseIfNeeded(Object)}, only that before checking for {@code object} being {@link Initialisable}, it * uses the given {@code muleContext} to perform further initialization. * <p> * It checks if the {@code object} implements {@link MuleContextAware}, in which case it will invoke * {@link MuleContextAware#setMuleContext(MuleContext)} with the given {@code muleContext}. * * @param object the object you're trying to initialise * @param muleContext a {@link MuleContext} * @throws InitialisationException * @throws IllegalArgumentException if {@code MuleContext} is {@code null} */ public static void initialiseIfNeeded(Object object, MuleContext muleContext) throws InitialisationException { initialiseIfNeeded(object, false, muleContext); } /** * The same as {@link #initialiseIfNeeded(Object)}, only that before checking for {@code object} being {@link Initialisable}, it * uses the given {@code muleContext} to perform further initialization. * <p> * It checks if the {@code object} implements {@link MuleContextAware}, in which case it will invoke * {@link MuleContextAware#setMuleContext(MuleContext)} with the given {@code muleContext}. * <p> * Also depending on the value of the {@code inject} argument, it will perform dependency injection on the {@code object} * * @param object the object you're trying to initialise * @param inject whether it should perform dependency injection on the {@code object} before actually initialising it * @param muleContext a {@link MuleContext} * @throws InitialisationException * @throws IllegalArgumentException if {@code MuleContext} is {@code null} */ public static void initialiseIfNeeded(Object object, boolean inject, MuleContext muleContext) throws InitialisationException { checkArgument(muleContext != null, "muleContext cannot be null"); object = unwrap(object); if (object == null) { return; } if (object instanceof MuleContextAware) { ((MuleContextAware) object).setMuleContext(muleContext); } if (inject) { try { muleContext.getInjector().inject(object); } catch (MuleException e) { I18nMessage message = createStaticMessage(format("Found exception trying to inject object of type '%s' on initialising phase", object.getClass().getName())); if (object instanceof Initialisable) { throw new InitialisationException(message, e, (Initialisable) object); } throw new MuleRuntimeException(message, e); } } initialiseIfNeeded(object); } /** * For each item in the {@code objects} collection, it invokes {@link #initialiseIfNeeded(Object)} * * @param objects the list of objects to be initialised * @throws InitialisationException */ public static void initialiseIfNeeded(Collection<? extends Object> objects) throws InitialisationException { initialiseIfNeeded(objects, null); } /** * For each item in the {@code objects} collection, it invokes {@link #initialiseIfNeeded(Object, MuleContext)} * * @param objects the list of objects to be initialised * @param muleContext a {@link MuleContext} * @throws InitialisationException */ public static void initialiseIfNeeded(Collection<? extends Object> objects, MuleContext muleContext) throws InitialisationException { try { doApplyPhase(Initialisable.PHASE_NAME, objects, muleContext, null); } catch (MuleException e) { throw (InitialisationException) e; } } /** * For each item in the {@code objects} collection, it invokes {@link #initialiseIfNeeded(Object, MuleContext)} * <p> * Also depending on the value of the {@code inject} argument, it will perform dependency injection on the {@code objects} * * @param objects the list of objects to be initialised * @param inject whether it should perform dependency injection on the {@code object} before actually initialising it * @param muleContext a {@link MuleContext} * @throws InitialisationException */ public static void initialiseIfNeeded(Collection<? extends Object> objects, boolean inject, MuleContext muleContext) throws InitialisationException { for (Object object : objects) { initialiseIfNeeded(object, inject, muleContext); } } /** * Invokes {@link Startable#start()} on {@code object} if it implements the {@link Startable} interface * * @param object the object you're trying to start * @throws MuleException */ public static void startIfNeeded(Object object) throws MuleException { object = unwrap(object); if (object instanceof Startable) { ((Startable) object).start(); } } /** * For each item in the {@code objects} collection, it invokes the the {@link Startable#start()} if it implements the * {@link Startable} interface. * * @param objects the list of objects to be started * @throws MuleException */ public static void startIfNeeded(Collection<? extends Object> objects) throws MuleException { doApplyPhase(Startable.PHASE_NAME, objects, null, null); } /** * For each item in the {@code objects} collection, it invokes the {@link Stoppable#stop()} if it implements the * {@link Stoppable} interface. * * @param objects the list of objects to be stopped * @throws MuleException */ public static void stopIfNeeded(Collection<? extends Object> objects) throws MuleException { doApplyPhase(Stoppable.PHASE_NAME, objects, null, null); } /** * For each item in the {@code objects} collection, it invokes the {@link Stoppable#stop()} if it implements the * {@link Stoppable} interface. * <p> * This method is considered safe because it will not throw exception and the {@link Stoppable#stop()} method will be called on * all the {@code objects} regarding on any (or all) of them throwing exceptions. Any exceptions generated will be logged using * the provided {@code logger} and processing will continue * * @param objects the list of objects to be stopped * @param logger the {@link Logger} in which any exception found is to be logged */ public static void safeStopIfNeeded(Collection<? extends Object> objects, Logger logger) { try { doApplyPhase(Stoppable.PHASE_NAME, objects, null, logger); } catch (Exception e) { logger.warn("Found unexpected exception during safe stop", e); } } /** * Invokes the {@link Stoppable#stop()} on {@code object} if it implements the {@link Stoppable} interface. * * @param object the object you're trying to stop * @throws MuleException */ public static void stopIfNeeded(Object object) throws MuleException { object = unwrap(object); if (object instanceof Stoppable) { ((Stoppable) object).stop(); } } /** * Invokes {@link Disposable#dispose()} on {@code object} if it implements the {@link Disposable} interface. If the dispose * operation fails, then the exception will be silently logged using the provided {@code logger} * * @param object the object you're trying to dispose */ public static void disposeIfNeeded(Object object, Logger logger) { object = unwrap(object); if (object instanceof Disposable) { try { ((Disposable) object).dispose(); } catch (Exception e) { logger.error("Exception found trying to dispose object. Shutdown will continue", e); } } } /** * For each item in the {@code objects} collection, it invokes {@link Disposable#dispose()} if it implements the * {@link Disposable} interface. * <p> * Per each dispose operation that fails, the exception will be silently logged using the provided {@code logger} * * @param objects the list of objects to be stopped * @throws MuleException */ public static void disposeIfNeeded(Collection<? extends Object> objects, Logger logger) { try { doApplyPhase(Disposable.PHASE_NAME, objects, null, logger); } catch (Exception e) { logger.error("Exception found trying to dispose object. Shutdown will continue", e); } } /** * Verifies that the given {@code muleContext} is not stopped or in the process of stopping * * @param muleContext the {@link MuleContext} to test * @param errorMessage the message of the {@link Exception} to be thrown if the assertion fails * @throws IllegalStateException if the {@code muleContext} is stopped or stopping * @since 4.0 */ public static void assertNotStopping(MuleContext muleContext, String errorMessage) { if (muleContext.isStopped() || muleContext.isStopping()) { throw new IllegalStateException(errorMessage); } } /** * Sets an objects {@link FlowConstruct} if it implements {@link FlowConstructAware}. * * @param object the object to inject the {@link FlowConstruct} into. * @param flowConstruct the {@link FlowConstruct} in which the object is defined. * @throws InitialisationException */ public static void setFlowConstructIfNeeded(Object object, FlowConstruct flowConstruct) { object = unwrap(object); if (object != null && object instanceof FlowConstructAware) { ((FlowConstructAware) object).setFlowConstruct(flowConstruct); } } /** * Sets an objects {@link FlowConstruct} if it implements {@link FlowConstructAware}. * * @param objects the objects to inject the {@link FlowConstruct} into. * @param flowConstruct the {@link FlowConstruct} in which the object is defined. * @throws InitialisationException */ public static void setFlowConstructIfNeeded(Collection<? extends Object> objects, FlowConstruct flowConstruct) { objects.forEach(o -> setFlowConstructIfNeeded(o, flowConstruct)); } /** * Sets an objects {@link MuleContext} if it implements {@link MuleContextAware}. * * @param object the object to inject the {@link MuleContext} into. * @param muleContext the {@link MuleContext} in which the object is defined. * @throws InitialisationException */ public static void setMuleContextIfNeeded(Object object, MuleContext muleContext) { object = unwrap(object); if (object != null && object instanceof MuleContextAware) { ((MuleContextAware) object).setMuleContext(muleContext); } } /** * Sets an objects {@link MuleContext} if it implements {@link MuleContextAware}. * * @param objects the objects to inject the {@link MuleContext} into. * @param muleContext the {@link MuleContext} in which the object is defined. * @throws InitialisationException */ public static void setMuleContextIfNeeded(Collection<? extends Object> objects, MuleContext muleContext) { objects.forEach(o -> setMuleContextIfNeeded(o, muleContext)); } private static void doApplyPhase(String phase, Collection<? extends Object> objects, MuleContext muleContext, Logger logger) throws MuleException { if (CollectionUtils.isEmpty(objects)) { return; } for (Object object : objects) { object = unwrap(object); if (object == null) { continue; } try { if (Initialisable.PHASE_NAME.equals(phase)) { if (muleContext != null) { initialiseIfNeeded(object, muleContext); } else { initialiseIfNeeded(object); } } else if (Startable.PHASE_NAME.equals(phase)) { startIfNeeded(object); } else if (Stoppable.PHASE_NAME.equals(phase)) { stopIfNeeded(object); } else if (Disposable.PHASE_NAME.equals(phase) && object instanceof Disposable) { disposeIfNeeded(object, logger); } } catch (MuleException e) { if (logger != null) { logger.error(format("Could not apply %s phase on object of class %s", phase, object.getClass().getName()), e); } else { throw e; } } } } private static Object unwrap(Object value) { if (value instanceof Optional) { Optional<?> optional = (Optional) value; return optional.isPresent() ? optional.get() : null; } return value; } }