/*
* 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.internal.lifecycle.phases;
import org.mule.runtime.api.lifecycle.LifecycleException;
import org.mule.runtime.core.api.lifecycle.LifecyclePhase;
import org.mule.runtime.core.api.lifecycle.LifecycleStateEnabled;
import org.mule.runtime.core.config.ExceptionHelper;
import org.mule.runtime.core.config.i18n.CoreMessages;
import org.mule.runtime.core.lifecycle.LifecycleObject;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Represents a configurable lifecycle phase. This is a default implementation of a 'generic phase' in that is can be configured
* to represnt any phase. Instances of this phase can then be registered with a
* {@link org.mule.runtime.core.api.lifecycle.LifecycleManager} and by used to enforce a lifecycle phase on an object. Usually,
* Lifecycle phases have a fixed configuration in which case a specialisation of this class should be created that initialises its
* configuration internally.
* <p>
* Note that this class and {@link org.mule.runtime.core.api.lifecycle.LifecycleTransitionResult} both make assumptions about the
* interfaces used - the return values and exceptions. These are, currently, that the return value is either void or
* {@link org.mule.runtime.core.api.lifecycle.LifecycleTransitionResult} and either 0 or 1 exceptions can be thrown which are
* either {@link InstantiationException} or {@link LifecycleException}.
*
* @see org.mule.runtime.core.api.lifecycle.LifecyclePhase
*/
public class DefaultLifecyclePhase implements LifecyclePhase {
protected transient final Logger logger = LoggerFactory.getLogger(DefaultLifecyclePhase.class);
private Class<?> lifecycleClass;
private final Method lifecycleMethod;
private Set<LifecycleObject> orderedLifecycleObjects = new LinkedHashSet<LifecycleObject>(6);
private Class<?>[] ignorredObjectTypes;
private final String name;
private final String oppositeLifecyclePhase;
private Set<String> supportedPhases;
public DefaultLifecyclePhase(String name, Class<?> lifecycleClass, String oppositeLifecyclePhase) {
this.name = name;
this.lifecycleClass = lifecycleClass;
// DefaultLifecyclePhase interface only has one method
lifecycleMethod = lifecycleClass.getMethods()[0];
this.oppositeLifecyclePhase = oppositeLifecyclePhase;
}
/**
* Subclasses can override this method to order <code>objects</code> before the lifecycle method is applied to them. This method
* does not apply any special ordering to <code>objects</code>.
*
* @param objects
* @param lo
* @return List with ordered objects
*/
protected List<?> sortLifecycleInstances(Collection<?> objects, LifecycleObject lo) {
return new ArrayList<Object>(objects);
}
@Override
public void addOrderedLifecycleObject(LifecycleObject lco) {
orderedLifecycleObjects.add(lco);
}
@Override
public void removeOrderedLifecycleObject(LifecycleObject lco) {
orderedLifecycleObjects.remove(lco);
}
protected boolean ignoreType(Class<?> type) {
if (ignorredObjectTypes == null) {
return false;
} else {
for (int i = 0; i < ignorredObjectTypes.length; i++) {
Class<?> ignorredObjectType = ignorredObjectTypes[i];
if (ignorredObjectType.isAssignableFrom(type)) {
return true;
}
}
}
return false;
}
@Override
public Set<LifecycleObject> getOrderedLifecycleObjects() {
return orderedLifecycleObjects;
}
@Override
public void setOrderedLifecycleObjects(Set<LifecycleObject> orderedLifecycleObjects) {
this.orderedLifecycleObjects = orderedLifecycleObjects;
}
@Override
public Class<?>[] getIgnoredObjectTypes() {
return ignorredObjectTypes;
}
@Override
public void setIgnoredObjectTypes(Class<?>[] ignorredObjectTypes) {
this.ignorredObjectTypes = ignorredObjectTypes;
}
@Override
public Class<?> getLifecycleClass() {
return lifecycleClass;
}
@Override
public void setLifecycleClass(Class<?> lifecycleClass) {
this.lifecycleClass = lifecycleClass;
}
@Override
public String getName() {
return name;
}
@Override
public Set<String> getSupportedPhases() {
return supportedPhases;
}
@Override
public void setSupportedPhases(Set<String> supportedPhases) {
this.supportedPhases = supportedPhases;
}
@Override
public void registerSupportedPhase(String phase) {
if (supportedPhases == null) {
supportedPhases = new HashSet<String>();
}
supportedPhases.add(phase);
}
@Override
public boolean isPhaseSupported(String phase) {
if (getSupportedPhases() == null) {
return false;
} else {
if (getSupportedPhases().contains(ALL_PHASES)) {
return true;
} else {
return getSupportedPhases().contains(phase);
}
}
}
@Override
public void applyLifecycle(Object o) throws LifecycleException {
if (o == null) {
return;
}
if (ignoreType(o.getClass())) {
return;
}
if (!getLifecycleClass().isAssignableFrom(o.getClass())) {
return;
}
if (o instanceof LifecycleStateEnabled) {
// If an object has its own lifecycle manager "LifecycleStateEnabled" it
// is possible that
// its state can be controlled outside the registry i.e. via JMX, double
// check here that we are
// not calling the same lifecycle twice
if (((LifecycleStateEnabled) o).getLifecycleState().isPhaseComplete(this.getName())) {
return;
} else if (!((LifecycleStateEnabled) o).getLifecycleState().isValidTransition(this.getName())) {
return;
}
}
try {
lifecycleMethod.invoke(o);
} catch (final Exception e) {
Throwable t = ExceptionHelper.unwrap(e);
if (t instanceof LifecycleException) {
throw (LifecycleException) t;
}
// Need to get the cause of the MuleException so the LifecycleException wraps a non-mule exception
throw new LifecycleException(CoreMessages.failedToInvokeLifecycle(lifecycleMethod.getName(), o),
ExceptionHelper.getNonMuleException(t), o);
}
}
@Override
public String getOppositeLifecyclePhase() {
return oppositeLifecyclePhase;
}
}