All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.omnifaces.services.pooled.PooledContext Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2021 OmniFaces
 *
 * 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
 *
 *     https://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.omnifaces.services.pooled;

import java.lang.annotation.Annotation;
import java.util.Map;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.stream.IntStream;

import jakarta.enterprise.context.spi.AlterableContext;
import jakarta.enterprise.context.spi.Contextual;
import jakarta.enterprise.context.spi.CreationalContext;
import jakarta.enterprise.inject.spi.Bean;

/**
 * The {@link AlterableContext} implementation for {@link Pooled} beans.
 */
public class PooledContext implements AlterableContext {

	private final ThreadLocal poolScope = ThreadLocal.withInitial(PooledScope::new);
	private final Map, InstancePool> instancePools = new ConcurrentHashMap<>();

	private final Map, Object> dummyInstances = new ConcurrentHashMap<>();

	@Override
	public Class getScope() {
		return Pooled.class;
	}

	@SuppressWarnings("unchecked")
	@Override
	public  T get(Contextual contextual, CreationalContext creationalContext) {
		if (contextual instanceof Bean) {
			PoolKey poolKey = poolScope.get().getPoolKey(contextual);

			if (poolKey == null) {
				return (T) dummyInstances.computeIfAbsent(contextual, ctx -> contextual.create(creationalContext));
			}

			return ((InstancePool) instancePools.get(poolKey.contextual())).getInstance(poolKey, creationalContext);
		}

		// TODO add clear error message and pick better exception
		throw new IllegalArgumentException();
	}

	@SuppressWarnings("unchecked")
	@Override
	public  T get(Contextual contextual) {
		if (contextual instanceof Bean) {
			PoolKey poolKey = poolScope.get().getPoolKey(contextual);

			if (poolKey == null) {
				return (T) dummyInstances.get(contextual);
			}

			return ((InstancePool) instancePools.get(poolKey.contextual())).getInstance(poolKey);
		}
		return null;
	}

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

	@SuppressWarnings("unchecked")
	 PoolKey allocateBean(Contextual contextual) {
		PoolKey poolKey = ((InstancePool) instancePools.get(contextual)).allocateInstance();

		poolScope.get().setPoolKey(poolKey);

		return poolKey;
	}

	@SuppressWarnings("unchecked")
	 void releaseBean(PoolKey poolKey) {
		poolScope.get().removePoolKey(poolKey);

		((InstancePool) instancePools.get(poolKey.contextual())).releaseInstance(poolKey);
	}

	boolean hasAllocatedInstanceOf(Bean bean) {
		return poolScope.get().getPoolKey(bean) != null;
	}

	public  void createInstancePool(Contextual contextual, Pooled poolSettings) {
		instancePools.put(contextual, new InstancePool<>(contextual, poolSettings));
	}

	 boolean mustDestroyBeanWhenCaught(Contextual contextual, Throwable throwable) {
		return instancePools.get(contextual).mustDestroyBeanWhenCaught(throwable);
	}

	@Override
	public void destroy(Contextual contextual) {
		PoolKey poolKey = poolScope.get().getPoolKey(contextual);

		if (poolKey != null) {
			destroyInstance(poolKey);
		}
	}

	private  void destroyInstance(PoolKey poolKey) {
		@SuppressWarnings("unchecked")
		InstancePool instancePool = (InstancePool) instancePools.get(poolKey.contextual());

		instancePool.destroyInstance(poolKey);
	}

	private static class InstancePool {

		private final Contextual contextual;
		private final Pooled poolSettings;

		private final Map, Instance> instances = new ConcurrentHashMap<>();
		private final BlockingDeque> freeInstanceKeys = new LinkedBlockingDeque<>();

		InstancePool(Contextual contextual, Pooled poolSettings) {
			this.contextual = contextual;
			this.poolSettings = poolSettings;

			IntStream.range(0, poolSettings.maxNumberOfInstances())
			         .mapToObj(i -> new PoolKey<>(contextual, i))
			         .forEach(freeInstanceKeys::add);
		}

		T getInstance(PoolKey poolKey) {
			if (!poolKey.contextual().equals(contextual)) {
				throw new IllegalArgumentException();
			}

			Instance instance = instances.get(poolKey);

			if (instance != null) {
				return instance.getInstance();
			}

			return null;
		}

		T getInstance(PoolKey poolKey, CreationalContext context) {
			if (!poolKey.contextual().equals(contextual)) {
				throw new IllegalArgumentException();
			}

			return instances.computeIfAbsent(poolKey, key -> new Instance<>(contextual, context)).getInstance();
		}

		PoolKey allocateInstance() {
			try {
				PoolKey poolKey = freeInstanceKeys.poll(poolSettings.instanceLockTimeout(), poolSettings.instanceLockTimeoutUnit());

				if (poolKey == null) {
					// Unable to allocate an instance within the configured timeout
					throw new PoolLockTimeoutException();
				}

				return poolKey;
			} catch (InterruptedException e) {
				throw new UncheckedInterruptedException(e);
			}
		}

		void releaseInstance(PoolKey key) {
			if (!contextual.equals(key.contextual())) {
				throw new IllegalArgumentException();
			}

			freeInstanceKeys.addFirst(key);
		}

		void destroyInstance(PoolKey key) {
			Instance instance = instances.remove(key);

			instance.destroy(contextual);
		}

		boolean mustDestroyBeanWhenCaught(Throwable throwable) {
			for (Class throwableType: poolSettings.dontDestroyOn()) {
				if (throwableType.isInstance(throwable)) {
					return false;
				}
			}

			if (poolSettings.dontDestroyOn().length > 0 && poolSettings.destroyOn().length == 0) {
				return true;
			}

			for (Class throwableType: poolSettings.destroyOn()) {
				if (throwableType.isInstance(throwable)) {
					return true;
				}
			}

			return false;
		}
	}

	private static class Instance {

		private final T instance;
		private final CreationalContext creationalContext;

		Instance(Contextual contextual, CreationalContext creationalContext) {
			this.instance = contextual.create(creationalContext);
			this.creationalContext = creationalContext;
		}

		T getInstance() {
			return instance;
		}

		CreationalContext getCreationalContext() {
			return creationalContext;
		}

		void destroy(Contextual contextual) {
			contextual.destroy(instance, creationalContext);
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy