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

org.springframework.pulsar.config.GenericReaderEndpointRegistry Maven / Gradle / Ivy

/*
 * Copyright 2023 the original author or 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 org.springframework.pulsar.config;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.SmartLifecycle;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.lang.Nullable;
import org.springframework.pulsar.reader.PulsarMessageReaderContainer;
import org.springframework.pulsar.reader.PulsarReaderContainerRegistry;
import org.springframework.util.Assert;

/**
 * Creates the necessary container instances for the registered
 * {@linkplain PulsarReaderEndpoint endpoints}. Also manages the lifecycle of the reader
 * containers, in particular within the lifecycle of the application context.
 *
 * 

* Contrary to containers created manually, reader listener containers managed by registry * are not beans in the application context and are not candidates for autowiring. Use * {@link #getReaderContainers()} ()} if you need to access this registry's reader * listener containers for management purposes. If you need to access to a specific reader * listener container, use {@link #getReaderContainer(String)} with the id of the * endpoint. * * @param container type * @param endpoint type * @author Soby Chacko * @author Chris Bono */ public class GenericReaderEndpointRegistry> implements PulsarReaderContainerRegistry, DisposableBean, SmartLifecycle, ApplicationContextAware, ApplicationListener { private final Class type; private final Map readerContainers = new ConcurrentHashMap<>(); private final ReentrantLock containersLock = new ReentrantLock(); private ConfigurableApplicationContext applicationContext; private int phase = C.DEFAULT_PHASE; private boolean contextRefreshed; private volatile boolean running; @SuppressWarnings("unchecked") protected GenericReaderEndpointRegistry(Class type) { this.type = (Class) type; } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { if (applicationContext instanceof ConfigurableApplicationContext) { this.applicationContext = (ConfigurableApplicationContext) applicationContext; } } @Override @Nullable public C getReaderContainer(String id) { Assert.hasText(id, "Container identifier must not be empty"); return this.readerContainers.get(id); } @Override public Set getReaderContainerIds() { return Collections.unmodifiableSet(this.readerContainers.keySet()); } @Override public Collection getReaderContainers() { return Collections.unmodifiableCollection(this.readerContainers.values()); } @Override public Collection getAllReaderContainers() { List containers = new ArrayList<>(getReaderContainers()); containers.addAll(this.applicationContext.getBeansOfType(this.type, true, false).values()); return containers; } public void registerReaderContainer(E endpoint, ReaderContainerFactory factory) { registerReaderContainer(endpoint, factory, false); } public void registerReaderContainer(E endpoint, ReaderContainerFactory factory, boolean startImmediately) { Assert.notNull(endpoint, "Endpoint must not be null"); Assert.notNull(factory, "Factory must not be null"); String id = endpoint.getId(); Assert.hasText(id, "Endpoint id must not be empty"); this.containersLock.lock(); try { Assert.state(!this.readerContainers.containsKey(id), "Another endpoint is already registered with id '" + id + "'"); C container = createReaderContainer(endpoint, factory); this.readerContainers.put(id, container); } finally { this.containersLock.unlock(); } } protected C createReaderContainer(E endpoint, ReaderContainerFactory factory) { C readerContainer = factory.createReaderContainer(endpoint); if (readerContainer instanceof InitializingBean) { try { ((InitializingBean) readerContainer).afterPropertiesSet(); } catch (Exception ex) { throw new BeanInitializationException("Failed to initialize message listener container", ex); } } int containerPhase = readerContainer.getPhase(); if (readerContainer.isAutoStartup() && containerPhase != C.DEFAULT_PHASE) { if (this.phase != C.DEFAULT_PHASE && this.phase != containerPhase) { throw new IllegalStateException("Encountered phase mismatch between container " + "factory definitions: " + this.phase + " vs " + containerPhase); } this.phase = readerContainer.getPhase(); } return readerContainer; } @Override public void destroy() throws Exception { for (C listenerContainer : getReaderContainers()) { listenerContainer.destroy(); } } // Delegating implementation of SmartLifecycle @Override public int getPhase() { return this.phase; } @Override public boolean isAutoStartup() { return true; } @Override public void start() { for (C listenerContainer : getReaderContainers()) { startIfNecessary(listenerContainer); } this.running = true; } @Override public void stop() { this.running = false; for (C listenerContainer : getReaderContainers()) { listenerContainer.stop(); } } @Override public void stop(Runnable callback) { this.running = false; Collection readerContainersToStop = getReaderContainers(); if (readerContainersToStop.size() > 0) { GenericReaderEndpointRegistry.AggregatingCallback aggregatingCallback = new GenericReaderEndpointRegistry.AggregatingCallback( readerContainersToStop.size(), callback); for (C readerContainer : readerContainersToStop) { if (readerContainer.isRunning()) { readerContainer.stop(aggregatingCallback); } else { aggregatingCallback.run(); } } } else { callback.run(); } } @Override public boolean isRunning() { return this.running; } @Override public void onApplicationEvent(ContextRefreshedEvent event) { if (event.getApplicationContext().equals(this.applicationContext)) { this.contextRefreshed = true; } } private void startIfNecessary(C listenerContainer) { if (this.contextRefreshed || listenerContainer.isAutoStartup()) { listenerContainer.start(); } } private static final class AggregatingCallback implements Runnable { private final AtomicInteger count; private final Runnable finishCallback; private AggregatingCallback(int count, Runnable finishCallback) { this.count = new AtomicInteger(count); this.finishCallback = finishCallback; } @Override public void run() { if (this.count.decrementAndGet() <= 0) { this.finishCallback.run(); } } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy