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

io.micronaut.context.scope.AbstractConcurrentCustomScope Maven / Gradle / Ivy

/*
 * Copyright 2017-2021 original authors
 *
 * 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 io.micronaut.context.scope;

import io.micronaut.context.BeanRegistration;
import io.micronaut.context.LifeCycle;
import io.micronaut.context.exceptions.BeanDestructionException;
import io.micronaut.core.annotation.NonNull;
import io.micronaut.core.annotation.Nullable;
import io.micronaut.core.util.CollectionUtils;
import io.micronaut.inject.BeanDefinition;
import io.micronaut.inject.BeanIdentifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.annotation.Annotation;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * Abstract implementation of the custom scope interface that simplifies defining new scopes using the Map interface.
 *
 * 

Note this implementation uses a single{@link ReentrantReadWriteLock} to lock the entire scope hence it is designed for scopes that will hold a small amount of beans. For implementations that hold many beans it is recommended to use a lock per {@link BeanIdentifier}.

* * @param The annotation type * @author graemerocher * @since 3.0.0 */ public abstract class AbstractConcurrentCustomScope implements CustomScope, LifeCycle>, AutoCloseable { private static final Logger LOG = LoggerFactory.getLogger(AbstractConcurrentCustomScope.class); private final Class annotationType; private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); private final Lock r = rwl.readLock(); private final Lock w = rwl.writeLock(); /** * A custom scope annotation. * * @param annotationType The annotation type */ protected AbstractConcurrentCustomScope(Class annotationType) { this.annotationType = Objects.requireNonNull(annotationType, "Annotation type cannot be null"); } /** * @param forCreation Whether it is for creation * @return Obtains the scope map, never null * @throws java.lang.IllegalStateException if the scope map cannot be obtained in the current context */ @NonNull protected abstract Map> getScopeMap(boolean forCreation); @Override public final Class annotationType() { return annotationType; } /** * Implement the close logic for the scope. */ @Override public abstract void close(); @NonNull @Override public final AbstractConcurrentCustomScope stop() { w.lock(); try { try { final Map> scopeMap = getScopeMap(false); destroyScope(scopeMap); } catch (IllegalStateException e) { // scope map not available in current context } close(); return this; } finally { w.unlock(); } } /** * Destroys the scope. * * @param scopeMap The scope map */ protected void destroyScope(@Nullable Map> scopeMap) { w.lock(); try { if (CollectionUtils.isNotEmpty(scopeMap)) { for (CreatedBean createdBean : scopeMap.values()) { try { createdBean.close(); } catch (BeanDestructionException e) { handleDestructionException(e); } } scopeMap.clear(); } } finally { w.unlock(); } } @SuppressWarnings("unchecked") @Override public final T getOrCreate(BeanCreationContext creationContext) { r.lock(); try { final Map> scopeMap = getScopeMap(true); final BeanIdentifier id = creationContext.id(); CreatedBean createdBean = scopeMap.get(id); if (createdBean != null) { return (T) createdBean.bean(); } else { r.unlock(); w.lock(); try { // re-check createdBean = scopeMap.get(id); if (createdBean != null) { r.lock(); return (T) createdBean.bean(); } else { try { createdBean = doCreate(creationContext); scopeMap.put(id, createdBean); } finally { r.lock(); } return (T) createdBean.bean(); } } finally { w.unlock(); } } } finally { r.unlock(); } } /** * Perform creation. * @param creationContext The creation context * @param The generic type * @return Created bean */ @NonNull protected CreatedBean doCreate(@NonNull BeanCreationContext creationContext) { return creationContext.create(); } @Override public final Optional remove(BeanIdentifier identifier) { if (identifier == null) { return Optional.empty(); } w.lock(); try { final Map> scopeMap; try { scopeMap = getScopeMap(false); } catch (IllegalStateException e) { return Optional.empty(); } if (CollectionUtils.isNotEmpty(scopeMap)) { final CreatedBean createdBean = scopeMap.get(identifier); if (createdBean != null) { try { createdBean.close(); } catch (BeanDestructionException e) { handleDestructionException(e); } //noinspection ConstantConditions return (Optional) Optional.ofNullable(createdBean.bean()); } else { return Optional.empty(); } } else { return Optional.empty(); } } finally { w.unlock(); } } /** * Method that can be overridden to customize what happens on a shutdown error. * @param e The exception */ protected void handleDestructionException(BeanDestructionException e) { LOG.error("Error occurred destroying bean of scope @{}: {}", annotationType.getSimpleName(), e.getMessage(), e); } @SuppressWarnings("unchecked") @Override public final Optional> findBeanRegistration(T bean) { r.lock(); try { final Map> scopeMap; try { scopeMap = getScopeMap(false); } catch (Exception e) { return Optional.empty(); } for (CreatedBean createdBean : scopeMap.values()) { if (createdBean.bean() == bean) { if (createdBean instanceof BeanRegistration) { return Optional.of((BeanRegistration) createdBean); } return Optional.of( new BeanRegistration<>( createdBean.id(), (BeanDefinition) createdBean.definition(), bean ) ); } } return Optional.empty(); } finally { r.unlock(); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy