public interface ExtensionService {

    /**
     * Return resource key for specified extension key.
     * If extension has no resource or extension with specified key doesn't exist the method
     * returns {@code null}.
     *
     * @param extensionKey             the extension key
     * @param extensionResourceVersion extension resource version or {@code null} if not used
     * @return resource key or {@code null} if not found
     */
    LepResourceKey getResourceKey(LepKey extensionKey, Version extensionResourceVersion);

    /**
     * Returns all extensions and its resource descriptors.
     *
     * @return all extensions and its resource descriptors
     */
    List<ExtensionResourceDescriptor> getExtensionResourceDescriptors();

    /**
     * Returns extension and its resource descriptor.
     *
     * @param extensionKey the extension key
     * @return extension and its resource descriptor
     */
    ExtensionResourceDescriptor getExtensionResourceDescriptor(LepKey extensionKey);

    /**
     * Return extension by key.
     *
     * @param key LEP key
     * @return extension instance
     */
    Extension getExtension(LepKey key);

    /**
     * Returns all extensions.
     *
     * @return all extensions
     */
    List<Extension> getExtensions();

    /**
     * Returns extensions grouped by group key.
     *
     * @param groupKeys group filter by key, can be {@code null} or empty
     * @return extensions grouped by group key
     */
    List<ExtensionGroup> getExtensionGroups(Set<LepKey> groupKeys);

    /**
     * Deletes extension by key.
     *
     * @param key LEP key
     */
    void deleteExtension(LepKey key);

} public interface LepExecutor {

    /**
     * Executes custom logic implementation for LEP.
     *
     * @param extensionKey             extension key
     * @param extensionResourceVersion extension resource version or {@code null} if not used
     * @param method                   method data on what LEP call occurs
     * @param managerService           LEP manager service object
     * @return LEP result value
     * @throws LepInvocationCauseException when LEP invocation resource throws exception
     */
    Object execute(LepKey extensionKey,
                   Version extensionResourceVersion,
                   LepMethod method,
                   LepManagerService managerService) throws LepInvocationCauseException;

    /**
     * Add processing listener to executor.
     *
     * @param listener the listener to add
     * @return {@code true} if the specified listener successfully added or executor already
     *     contains the specified listener
     */
    boolean registerExecutorListener(LepExecutorListener listener);

    /**
     * Removes processing listener from executor.
     *
     * @param listener the listener to remove
     * @return {@code true} if this executor contained the specified listener
     */
    boolean unregisterExecutorListener(LepExecutorListener listener);

} public class LepInvocationCauseException extends Exception {

    /**
     * Serial UID.
     */
    private static final long serialVersionUID = -6216272704551111089L;

    /**
     * This field holds the cause if the
     * LepInvocationCauseException(Throwable lepCause) constructor was
     * used to instantiate the object.
     */
    private Exception lepCause;

    /**
     * Constructs an {@code LepInvocationCauseException} with
     * {@code null} as the target exception.
     */
    private LepInvocationCauseException() {
        super((Exception) null);  // Disallow initCause
    }

    /**
     * Constructs a LepInvocationCauseException with a LEP exception.
     *
     * @param lepCause the LEP exception
     */
    public LepInvocationCauseException(Exception lepCause) {
        super((Exception) null);  // Disallow initCause
        this.lepCause = lepCause;
    }

    /**
     * Constructs a InvocationTargetException with a target exception
     * and a detail message.
     *
     * @param lepCause the LEP exception
     * @param msg      the detail message
     */
    public LepInvocationCauseException(Exception lepCause, String msg) {
        super(msg, null);  // Disallow initCause
        this.lepCause = lepCause;
    }

    /**
     * Returns the cause of this exception (the LEP thrown cause exception,
     * which may be {@code null}).
     *
     * @return the cause of this exception.
     */
    @Override
    public Exception getCause() {
        return lepCause;
    }

} public interface LepKey {

    /**
     * Returns LEP identificator.
     *
     * @return LEP identificator
     */
    String getId();

    /**
     * Returns group key to which belongs this LepKey.
     *
     * <p>For top level group or if group not specified for this LepKey instance
     * the method can return {@code null}.
     *
     * @return group key or {@code null}
     */
    LepKey getGroupKey();

} public interface LepMethod {

    Object getTarget();

    MethodSignature getMethodSignature();

    Object[] getMethodArgValues();

} public static class AfterExecutionEvent extends BaseLepProcessingEvent {

    private static final long serialVersionUID = -7900329326131385686L;

    private final transient Object resultValue;

    public AfterExecutionEvent(LepManager source,
                               LepKey key,
                               LepMethod method,
                               Object resultValue) {
        super(source, key, method);
        this.resultValue = resultValue;
    }

    public Object getResultValue() {
        return resultValue;
    }

    // avoid findbug warning on transient
    private void writeObject(java.io.ObjectOutputStream stream) throws java.io.IOException {
        throw new java.io.NotSerializableException(getClass().getName());
    }

    // avoid findbug warning on transient
    private void readObject(java.io.ObjectInputStream stream) throws java.io.IOException,
        ClassNotFoundException {
        throw new java.io.NotSerializableException(getClass().getName());
    }

} public static class AfterProcessingEvent extends BaseLepProcessingEvent {

    private static final long serialVersionUID = -1615716251427743997L;

    private final Exception exception;
    private final transient Object resultValue;

    /**
     * Base constructor.
     *
     * @param source      event source
     * @param key         resource key
     * @param method      LEP method
     * @param exception   method exception or null
     * @param resultValue method result
     */
    public AfterProcessingEvent(LepManager source,
                                LepKey key,
                                LepMethod method,
                                Exception exception,
                                Object resultValue) {
        super(source, key, method);
        this.resultValue = resultValue;
        this.exception = exception;
    }

    public Object getResultValue() {
        return resultValue;
    }

    public boolean isError() {
        return exception == null;
    }

    public Exception getException() {
        return exception;
    }

    // avoid findbug warning on transient
    private void writeObject(java.io.ObjectOutputStream stream) throws java.io.IOException {
        throw new java.io.NotSerializableException(getClass().getName());
    }

    // avoid findbug warning on transient
    private void readObject(java.io.ObjectInputStream stream) throws java.io.IOException,
        ClassNotFoundException {
        throw new java.io.NotSerializableException(getClass().getName());
    }

} public static class BeforeExecutionEvent extends BaseLepProcessingEvent {

    private static final long serialVersionUID = -8080910211496219122L;

    public BeforeExecutionEvent(LepManager source,
                                LepKey key,
                                LepMethod method) {
        super(source, key, method);
    }

} public static class BeforeProcessingEvent extends BaseLepProcessingEvent {

    private static final long serialVersionUID = 6302184998762436336L;

    public BeforeProcessingEvent(LepManager source,
                                 LepKey key,
                                 LepMethod method) {
        super(source, key, method);
    }

} public interface LepProcessingListener extends Consumer<LepProcessingEvent> {

} public interface LepResourceService {

    default boolean isResourceExists(ContextsHolder contextsHolder, LepResourceKey resourceKey) {
        return getResourceDescriptor(contextsHolder, resourceKey) != null;
    }

    LepResourceDescriptor getResourceDescriptor(ContextsHolder contextsHolder, LepResourceKey resourceKey);

    LepResource getResource(ContextsHolder contextsHolder, LepResourceKey resourceKey);

    /**
     * Save extension resource.
     * save = create or update.
     *
     * @param contextsHolder LEP contexts holder
     * @param extensionKey   LEP extension key
     * @param resource       LEP resource instance
     * @return return extension resource
     */
    LepResource saveResource(ContextsHolder contextsHolder, LepKey extensionKey, LepResource resource);

    List<Version> getResourceVersions(ContextsHolder contextsHolder, LepResourceKey resourceKey);

}
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.*;
import com.icthh.xm.lep.api.ExtensionService;
import com.icthh.xm.lep.api.LepExecutor;
import com.icthh.xm.lep.api.LepInvocationCauseException;
import com.icthh.xm.lep.api.LepKey;
import com.icthh.xm.lep.api.LepMethod;
import com.icthh.xm.lep.api.LepProcessingEvent.AfterExecutionEvent;
import com.icthh.xm.lep.api.LepProcessingEvent.AfterProcessingEvent;
import com.icthh.xm.lep.api.LepProcessingEvent.BeforeExecutionEvent;
import com.icthh.xm.lep.api.LepProcessingEvent.BeforeProcessingEvent;
import com.icthh.xm.lep.api.LepProcessingListener;
import com.icthh.xm.lep.api.LepResourceService;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.mockito.InOrder;
package com.icthh.xm.lep.core;



/**
 * The {@link CoreLepManagerProcessingListenerTest} class.
 */
public class CoreLepManagerProcessingListenerTest {

    private CoreLepManager manager;

    @Rule
    public ExpectedException thrown = ExpectedException.none();

    @Before
    public void beforeEachTest() {
        manager = new CoreLepManager();
    }

    @After
    public void afterEachTest() {
        manager.destroy();
    }

    @Test
    public void returnFalseOnRemoveUnregisteredProcessingListener() {
        LepProcessingListener unregisteredListener = mock(LepProcessingListener.class);