Package org.jrebirth.af.core.link

Source Code of org.jrebirth.af.core.link.AbstractWaveReady

/**
* Get more info at : www.jrebirth.org .
* Copyright JRebirth.org © 2011-2013
* Contact : sebastien.bordes@jrebirth.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jrebirth.af.core.link;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.jrebirth.af.core.annotation.BeforeInit;
import org.jrebirth.af.core.annotation.OnRelease;
import org.jrebirth.af.core.annotation.SkipAnnotation;
import org.jrebirth.af.core.command.Command;
import org.jrebirth.af.core.command.CommandBean;
import org.jrebirth.af.core.concurrent.AbstractJrbRunnable;
import org.jrebirth.af.core.concurrent.JRebirth;
import org.jrebirth.af.core.exception.CoreException;
import org.jrebirth.af.core.exception.JRebirthThreadException;
import org.jrebirth.af.core.facade.JRebirthEventType;
import org.jrebirth.af.core.facade.WaveReady;
import org.jrebirth.af.core.log.JRLogger;
import org.jrebirth.af.core.log.JRLoggerFactory;
import org.jrebirth.af.core.service.Service;
import org.jrebirth.af.core.ui.Model;
import org.jrebirth.af.core.util.CheckerUtility;
import org.jrebirth.af.core.util.ClassUtility;
import org.jrebirth.af.core.wave.Wave;
import org.jrebirth.af.core.wave.Wave.Status;
import org.jrebirth.af.core.wave.WaveBase;
import org.jrebirth.af.core.wave.WaveBean;
import org.jrebirth.af.core.wave.WaveData;
import org.jrebirth.af.core.wave.WaveGroup;
import org.jrebirth.af.core.wave.WaveType;
import org.jrebirth.af.core.wave.checker.WaveChecker;

/**
*
* The class <strong>AbstractWaveReady</strong>.
*
* This is the base class for all of each of JRebirth pattern subclasses.<br />
* It allow to send waves.
*
* All things related to wave management must be execute into the JRebirth Thread
*
* @author Sébastien Bordes
*
* @param <R> the class type of the subclass
*/
@SkipAnnotation(false)
public abstract class AbstractWaveReady<R extends WaveReady<R>> extends AbstractReady<R> implements WaveReady<R>, LinkMessages {

    /** The default fallback wave handle method. */
    public static final String PROCESS_WAVE_METHOD_NAME = "processWave";

    /** The class logger. */
    private static final JRLogger LOGGER = JRLoggerFactory.getLogger(AbstractWaveReady.class);

    /** The return wave type map. */
    private final Map<WaveType, WaveType> returnWaveTypeMap = new HashMap<>();

    /** The return command class map. */
    private final Map<WaveType, Class<? extends Command>> returnCommandClass = new HashMap<>();

    /** A map that store all annotated methods to call sorted by lifecycle phase. */
    private Map<String, List<Method>> lifecycleMethod;

    /**
     * Short cut method used to retrieve the notifier.
     *
     * @return the notifier retrieved from global facade
     */
    private Notifier getNotifier() {
        return getLocalFacade().getGlobalFacade().getNotifier();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final void listen(final WaveType... waveTypes) {
        // Call the other method with null waveChecker & method
        listen(null, null, waveTypes);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final void listen(final WaveChecker waveChecker, final WaveType... waveTypes) {
        // Call the other method with null method
        listen(waveChecker, null, waveTypes);
    }

    /**
     * Return the human-readable list of Wave Type.
     *
     * @param waveTypes the list of wave type
     *
     * @return the string list of Wave Type
     */
    private String getWaveTypesString(final WaveType[] waveTypes) {
        final StringBuilder sb = new StringBuilder();
        for (final WaveType waveType : waveTypes) {
            sb.append(waveType.toString()).append(" ");
        }
        return sb.toString();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final void listen(final WaveChecker waveChecker, final Method method, final WaveType... waveTypes) {

        // Check API compliance
        CheckerUtility.checkWaveTypeContract(this.getClass(), waveTypes);

        final WaveReady<?> waveReady = this;

        LOGGER.trace(LinkMessages.LISTEN_WAVE_TYPE, getWaveTypesString(waveTypes), waveReady.getClass().getSimpleName());

        // Use the JRebirth Thread to add new subscriptions for given Wave Type
        JRebirth.runIntoJIT(new AbstractJrbRunnable(LISTEN_WAVE_TYPE.getText(getWaveTypesString(waveTypes), waveReady.getClass().getSimpleName())) {
            @Override
            public void runInto() throws JRebirthThreadException {
                getNotifier().listen(waveReady, waveChecker, method, waveTypes);
            }
        });
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final void registerCallback(final WaveType callType, final WaveType responseType) {

        // Call the generic method
        registerCallback(null, callType, responseType, null);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final void registerCallback(final WaveType callType, final WaveType responseType, final Class<? extends Command> returnCommandClass) {

        // Call the generic method
        registerCallback(null, callType, responseType, returnCommandClass);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final void registerCallback(final WaveChecker waveChecker, final WaveType callType, final WaveType responseType, final Class<? extends Command> returnCommandClass) {

        // Perform the subscription
        listen(waveChecker, callType);

        // Store a link between call Wave Type and return wave type that store the result type ino a WaveItem
        this.returnWaveTypeMap.put(callType, responseType);

        // Store the Command Class that will handle the service result (optional)
        if (returnCommandClass != null) {
            this.returnCommandClass.put(callType, returnCommandClass);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final WaveType getReturnWaveType(final WaveType waveType) {
        return this.returnWaveTypeMap.get(waveType);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final Class<? extends Command> getReturnCommand(final WaveType waveType) {
        return this.returnCommandClass.get(waveType);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final void unlisten(final WaveType... waveTypes) {

        // Store an hard link to be able to use current class into the closure
        final WaveReady waveReady = this;

        LOGGER.trace(LinkMessages.UNLISTEN_WAVE_TYPE, getWaveTypesString(waveTypes), waveReady.getClass().getSimpleName());

        // Use the JRebirth Thread to manage Waves
        JRebirth.runIntoJIT(new AbstractJrbRunnable(UNLISTEN_WAVE_TYPE.getText(getWaveTypesString(waveTypes), waveReady.getClass().getSimpleName())) {

            @Override
            protected void runInto() throws JRebirthThreadException {
                getNotifier().unlisten(waveReady, waveTypes);
            }
        });
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final void sendWave(final Wave wave) {
        // Define the from class if it didn't been done before (manually)
        if (wave.getFromClass() == null) {
            wave.setFromClass(this.getClass());
        }
        sendWaveIntoJit(wave);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final Wave sendWave(final WaveType waveType, final WaveData<?>... waveData) {
        return sendWaveIntoJit(createWave(WaveGroup.UNDEFINED, waveType, null, waveData));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final <WB extends WaveBean> Wave callCommand(final Class<? extends CommandBean<WB>> commandClass, final WB waveBean) {
        return sendWaveIntoJit(createWave(WaveGroup.CALL_COMMAND, null, commandClass, waveBean));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final Wave callCommand(final Class<? extends Command> commandClass, final WaveData<?>... data) {
        return sendWaveIntoJit(createWave(WaveGroup.CALL_COMMAND, null, commandClass, data));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final Wave returnData(final Class<? extends Service> serviceClass, final WaveType waveType, final WaveData<?>... data) {
        return sendWaveIntoJit(createWave(WaveGroup.RETURN_DATA, waveType, serviceClass, data));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final Wave attachUi(final Class<? extends Model> modelClass, final WaveData<?>... data) {
        return sendWaveIntoJit(createWave(WaveGroup.ATTACH_UI, null, modelClass, data));
    }

    /**
     * Send the given wave using the JRebirth Thread.
     *
     * @param wave the wave to send
     *
     * @return the wave sent to JIT (with Sent status)
     */
    private Wave sendWaveIntoJit(final Wave wave) {

        CheckerUtility.checkWave(wave);

        wave.setStatus(Status.Sent);

        // Use the JRebirth Thread to manage Waves
        JRebirth.runIntoJIT(new AbstractJrbRunnable(SEND_WAVE.getText(wave.toString())) {
            @Override
            public void runInto() throws JRebirthThreadException {
                getNotifier().sendWave(wave);
            }
        });

        return wave;
    }

    /**
     * Build a wave object.
     *
     * @param waveGroup the group of the wave
     * @param waveType the type of the wave
     * @param relatedClass the related class if any
     * @param waveData wave data to use
     *
     * @return the wave built
     */
    private Wave createWave(final WaveGroup waveGroup, final WaveType waveType, final Class<?> relatedClass, final WaveData<?>... waveData) {

        final Wave wave = new WaveBase();

        wave.setWaveGroup(waveGroup);
        wave.setWaveType(waveType);
        wave.setFromClass(this.getClass());
        wave.setRelatedClass(relatedClass);

        for (final WaveData<?> wd : waveData) {
            wave.addData(wd);
        }

        // Track wave creation
        getLocalFacade().getGlobalFacade().trackEvent(JRebirthEventType.CREATE_WAVE, this.getClass(), wave.getClass());

        return wave;
    }

    /**
     * Build a wave object with its dedicated WaveBean.
     *
     * @param waveGroup the group of the wave
     * @param waveType the type of the wave
     * @param relatedClass the related class if any
     * @param waveBean the wave bean that holds all required wave data
     *
     * @return the wave built
     */
    private Wave createWave(final WaveGroup waveGroup, final WaveType waveType, final Class<?> relatedClass, final WaveBean waveBean) {

        final Wave wave = new WaveBase();

        wave.setWaveGroup(waveGroup);
        wave.setWaveType(waveType);
        wave.setFromClass(this.getClass());
        wave.setRelatedClass(relatedClass);

        wave.linkWaveBean(waveBean);

        // Track wave creation
        getLocalFacade().getGlobalFacade().trackEvent(JRebirthEventType.CREATE_WAVE, this.getClass(), wave.getClass());

        return wave;
    }

    // /**
    // * This method is called before each execution of the command.
    // *
    // * It will parse the given wave to store local command properties.
    // *
    // * Wave parsing mechanism is composed by three steps:
    // * <ol>
    // * <li>Parse WaveBean properties and copy them into command ones if they exist (by reflection)</li>
    // * <li>Parse WaveData keys and copy them into command ones if they exist (by reflection)</li>
    // * <li>Call {@link #parseWave(Wave)} method for later customization</li>
    // * </ol>
    // *
    // * @param wave the wave to parse
    // */
    // private void parseInternalWave(final Wave wave) {
    //
    // // Parse WaveBean
    // // WB waveBean = getWaveBean(wave);
    // // if (waveBean != null && !(waveBean instanceof DefaultWaveBean)) {
    // // for (Field f : ClassUtility.retrievePropertyList(waveBean.getClass())) {
    // // try {
    // // tryToSetProperty(f.getName(), f.get(waveBean));
    // // } catch (IllegalArgumentException | IllegalAccessException e) {
    // // LOGGER.error("Fail to get field value " + f.getName() + " from " + waveBean.getClass(), e);
    // // }
    // // }
    // // }
    //
    // // Parse WaveData
    // for (final WaveData<?> wd : wave.getWaveItems()) {
    // tryToSetProperty(wd.getKey().toString(), wd.getValue());
    // }
    //
    // // Call customized method
    // // parseWave(wave);
    // }
    //
    // /**
    // * Try to set the value of the given property for the current class.
    // *
    // * @param fieldName the field to initialize
    // * @param fieldValue the field value to set
    // */
    // private void tryToSetProperty(final String fieldName, final Object fieldValue) {
    // try {
    // this.getClass().getField(fieldName).set(this, fieldValue);
    // } catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException e) {
    // LOGGER.error("Fail to set value for field " + fieldName, e);
    // }
    // }

    /**
     * Customizable method used to perform more action before command execution.
     *
     * @param wave the given wave to parser before command execution
     */
    // protected abstract void parseWave(final Wave wave);

    // /**
    // * {@inheritDoc}
    // */
    // @Override
    // public final void handle(final Wave wave) throws WaveException {
    // try {
    // // Build parameter list of the searched method
    // final List<Object> parameterValues = new ArrayList<>();
    // for (final WaveData<?> wd : wave.getWaveItems()) {
    // // Add only wave items defined as parameter
    // if (wd.getKey().isParameter()) {
    // parameterValues.add(wd.getValue());
    // }
    // }
    // // Add the current wave to process
    // parameterValues.add(wave);
    //
    // // Search the wave handler method
    // final Method method = ClassUtility.getMethodByName(this.getClass(), ClassUtility.underscoreToCamelCase(wave.getWaveType().toString()));
    // if (method != null) {
    // // Call this method with right parameters
    // method.invoke(this, parameterValues.toArray());
    // }
    // } catch (final NoSuchMethodException e) {
    //
    // LOGGER.info(CUSTOM_METHOD_NOT_FOUND, e.getMessage());
    // // If no method was found, call the default method
    // processWave(wave);
    //
    // } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
    // LOGGER.error(WAVE_DISPATCH_ERROR, e);
    // // Propagate the wave exception
    // throw new WaveException(wave, e);
    // }
    // }

    /**
     * Process the wave. Typically by using a switch on the waveType.
     *
     * @param wave the wave received
     */
    protected abstract void processWave(final Wave wave);

    /**
     * {@inheritDoc}
     */
    @SuppressWarnings("unchecked")
    @Override
    public void setup() throws CoreException {

        if (ComponentEnhancer.canProcessAnnotation((Class<? extends WaveReady<?>>) this.getClass())) {

            // Search Singleton and Multiton annotation on field
            ComponentEnhancer.injectComponent(this);

            // Attach custom method configured with custom Lifecycle annotation
            this.lifecycleMethod = ComponentEnhancer.defineLifecycleMethod(this);

            // Search OnWave annotation to manage auto wave handler setup
            ComponentEnhancer.manageOnWaveAnnotation(this);

        }

        callAnnotatedMethod(BeforeInit.class);

        ready();

        callAnnotatedMethod(BeforeInit.class);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void release() {

        setKey(null);

        callAnnotatedMethod(OnRelease.class);
    }

    /**
     * Call annotated methods corresponding at given lifecycle annotation.
     *
     * @param annotationClass the annotation related to the lifecycle
     */
    private void callAnnotatedMethod(final Class<? extends Annotation> annotationClass) {
        if (this.lifecycleMethod.get(annotationClass.getName()) != null) {
            for (final Method method : this.lifecycleMethod.get(annotationClass.getName())) {

                try {
                    ClassUtility.callMethod(method, this);
                } catch (final CoreException e) {
                    LOGGER.error(CALL_ANNOTATED_METHOD_ERROR, e);
                }
            }
        }
    }

    /**
     * The component is now ready to do custom initialization tasks.
     *
     * @throws CoreException if an error occurred
     */
    protected abstract void ready() throws CoreException;

}
TOP

Related Classes of org.jrebirth.af.core.link.AbstractWaveReady

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.