de.ks.activity.context.ActivityContext.java Source code

Java tutorial

Introduction

Here is the source code for de.ks.activity.context.ActivityContext.java

Source

/*
 * Copyright [2014] [Christian Loehnert, krampenschiesser@gmail.com]
 * 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 de.ks.activity.context;

import de.ks.activity.executor.ActivityExecutor;
import de.ks.activity.executor.ActivityJavaFXExecutor;
import de.ks.eventsystem.bus.EventBus;
import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.enterprise.context.spi.Context;
import javax.enterprise.context.spi.Contextual;
import javax.enterprise.context.spi.CreationalContext;
import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.BeanManager;
import javax.enterprise.inject.spi.CDI;
import java.lang.annotation.Annotation;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 *
 */
public class ActivityContext implements Context {
    private static final Logger log = LoggerFactory.getLogger(ActivityContext.class);
    private static final AtomicBoolean registeredWithEventBus = new AtomicBoolean();

    protected final ConcurrentHashMap<String, ActivityHolder> activities = new ConcurrentHashMap<>();
    protected volatile String currentActivity = null;

    private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);
    private final BeanManager beanManager;

    public static void start(String id) {
        CDI<Object> cdi = CDI.current();
        ActivityContext context = (ActivityContext) cdi.getBeanManager().getContext(ActivityScoped.class);
        context.startActivity(id);

        if (!registeredWithEventBus.get()) {
            EventBus eventBus = cdi.select(EventBus.class).get();
            ActivityExecutor activityExecutor = cdi.select(ActivityExecutor.class).get();
            ActivityJavaFXExecutor fxExecutor = cdi.select(ActivityJavaFXExecutor.class).get();
            eventBus.setExecutorService(activityExecutor);
            eventBus.setExecutorService(fxExecutor);
        }
    }

    public static void stop(String id) {
        ActivityContext context = (ActivityContext) CDI.current().getBeanManager().getContext(ActivityScoped.class);
        context.stopActivity(id);
    }

    public static void cleanup(String id) {
        ActivityContext context = (ActivityContext) CDI.current().getBeanManager().getContext(ActivityScoped.class);
        context.cleanupSingleActivity(id);
    }

    public static void stopAll() {
        ActivityContext context = (ActivityContext) CDI.current().getBeanManager().getContext(ActivityScoped.class);
        context.cleanupAllActivities();
    }

    public ActivityContext(BeanManager mgr) {
        beanManager = mgr;
    }

    @Override
    public Class<? extends Annotation> getScope() {
        return ActivityScoped.class;
    }

    @Override
    public <T> T get(Contextual<T> contextual) {
        if (contextual instanceof Bean) {
            Bean bean = (Bean) contextual;

            Pair<String, Class<?>> key = getKey(bean);
            lock.readLock().lock();
            try {
                StoredBean storedBean = activities.get(key.getLeft()).getStoredBean(key.getRight());
                if (storedBean != null) {
                    return storedBean.getInstance();
                }
            } finally {
                lock.readLock().unlock();
            }
        }
        return null;
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T> T get(Contextual<T> contextual, CreationalContext<T> creationalContext) {
        if (contextual instanceof Bean) {
            Bean bean = (Bean) contextual;
            Pair<String, Class<?>> key = getKey(bean);

            lock.writeLock().lock();
            try {
                Object o = bean.create(creationalContext);
                StoredBean storedBean = new StoredBean(bean, creationalContext, o);
                activities.get(key.getLeft()).put(key.getRight(), storedBean);
                return (T) o;
            } finally {
                lock.writeLock().unlock();
            }
        }
        return null;
    }

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

    protected Pair<String, Class<?>> getKey(Bean<?> bean) {
        Class<?> beanClass = bean.getBeanClass();
        Annotation annotation = beanClass.getAnnotation(ActivityScoped.class);

        if (annotation == null) {//might be a producer method, only warn

            String msg = "Unable to retrieve " + ActivityScoped.class.getName() + " from " + beanClass;
            if (bean.getClass().getName().contains("Producer")) {
                log.trace(msg);
            } else {
                log.warn(msg);
            }
        }
        if (currentActivity == null) {
            throw new IllegalStateException("No activity currently active!");
        }
        return Pair.of(currentActivity, beanClass);
    }

    public void cleanupSingleActivity(String id) {
        lock.writeLock().lock();
        try {
            ActivityHolder activityHolder = activities.remove(id);
            log.debug("Cleanup activity {}", activityHolder.getId());
            Set<Map.Entry<Class<?>, StoredBean>> entries = activityHolder.objectStore.entrySet();

            for (Iterator<Map.Entry<Class<?>, StoredBean>> iterator = entries.iterator(); iterator.hasNext();) {
                Map.Entry<Class<?>, StoredBean> next = iterator.next();
                next.getValue().destroy();
                iterator.remove();
            }
        } finally {
            lock.writeLock().unlock();
        }
    }

    public void cleanupSingleBean(Class<?> clazz) {
        lock.writeLock().lock();
        try {
            ActivityHolder activityHolder = activities.get(getCurrentActivity());

            StoredBean storedBean = activityHolder.objectStore.get(clazz);
            if (storedBean != null) {
                storedBean.destroy();
                activityHolder.objectStore.remove(clazz);
                log.debug("Cleaned up bean {} of activity {}", clazz.getName(), activityHolder.getId());
            }
        } finally {
            lock.writeLock().unlock();
        }
    }

    public ActivityHolder startActivity(String id) {
        lock.writeLock().lock();
        try {
            currentActivity = id;
            if (activities.containsKey(id)) {
                log.debug("Resuming activity {}", id);
                return activities.get(id);
            } else {
                ActivityHolder holder = new ActivityHolder(id);
                this.activities.put(id, holder);

                log.debug("Started activity {}", holder.getId());
                return holder;
            }

        } finally {
            lock.writeLock().unlock();
        }
    }

    public void stopActivity(String id) {
        lock.writeLock().lock();
        try {
            ActivityHolder activityHolder = activities.get(id);
            if (activityHolder == null) {
                log.warn("Activity {} is already stopped", id);
                return;
            }
            int count = activityHolder.getCount().decrementAndGet();
            if (count == 0) {
                cleanupSingleActivity(id);
                currentActivity = null;
                log.debug("Stopped activity {}", activityHolder.getId());
            } else {
                log.debug("Don't stop activity {} because of {} holders.", activityHolder.getId(), count);
            }
        } finally {
            lock.writeLock().unlock();
        }
    }

    public void cleanupAllActivities() {
        log.debug("Cleanup all activities.");
        for (String id : activities.keySet()) {
            ActivityHolder activityHolder = activities.get(id);
            if (activityHolder == null) {
                log.error("No activity active in thread {}", Thread.currentThread().getName());
                throw new IllegalStateException("No activity active in thread " + Thread.currentThread().getName());
            }
            if (multipleThreadsActive(activityHolder)) {
                log.warn(
                        "There are still {} other threads holding a reference to this activity, cleanup not allowed",
                        activityHolder.getCount().get() - 1);
            }
            waitForOtherThreads(activityHolder);
            cleanupSingleActivity(id);
        }
        lock.writeLock().lock();
        try {
            this.activities.clear();
        } finally {
            lock.writeLock().unlock();
        }
    }

    private void waitForOtherThreads(ActivityHolder activityHolder) {
        while (multipleThreadsActive(activityHolder)) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                log.error("Interrupted", e);
            }
        }
    }

    private boolean multipleThreadsActive(ActivityHolder activityHolder) {
        return activityHolder.getCount().get() > 1;
    }

    public ActivityHolder getHolder() {
        if (currentActivity == null) {
            throw new RuntimeException("No activity active in current thread!");
        }
        return activities.get(currentActivity);
    }

    public String getCurrentActivity() {
        return currentActivity;
    }

    public boolean hasCurrentActivity() {
        return currentActivity != null;
    }
}