io.github.lxgaming.common.inject.ServiceProviderImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of common-inject Show documentation
Show all versions of common-inject Show documentation
Lightweight* dependency injection framework for Java
/*
* Copyright 2022 Alex Thomson
*
* 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 io.github.lxgaming.common.inject;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Deque;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ServiceProviderImpl implements ServiceProvider, AutoCloseable {
protected final Collection descriptors;
protected final Map instances;
protected final Collection closeables;
protected final Deque deque;
protected final Lock lock;
protected ServiceProviderImpl parent;
protected ServiceProviderImpl(@NotNull ServiceProviderImpl parent) {
this(parent.descriptors);
this.parent = parent;
}
protected ServiceProviderImpl(@NotNull Collection descriptors) {
this.descriptors = descriptors;
this.instances = new HashMap<>();
this.closeables = new ArrayList<>();
this.deque = new ArrayDeque<>();
this.lock = new ReentrantLock();
}
public @NotNull ServiceScope createScope() {
return new ServiceScope(new ServiceProviderImpl(parent != null ? parent : this));
}
public @NotNull T getRequiredService(@NotNull Class serviceClass) throws IllegalStateException {
T service = getService(serviceClass);
if (service == null) {
throw new IllegalStateException(String.format("No service for '%s' has been registered", serviceClass));
}
return service;
}
@SuppressWarnings("unchecked")
public @Nullable T getService(@NotNull Class serviceClass) {
if (serviceClass.isInstance(this)) {
return (T) this;
}
ServiceDescriptor descriptor = getDescriptor(serviceClass);
if (descriptor == null) {
return null;
}
return getInstance(descriptor);
}
public @NotNull List getServices(@NotNull Class serviceClass) {
List services = new ArrayList<>();
for (ServiceDescriptor descriptor : descriptors) {
if (descriptor.getServiceClass() == serviceClass) {
services.add(getRequiredService(serviceClass));
}
}
return services;
}
protected @Nullable ServiceDescriptor getDescriptor(@NotNull Class> serviceClass) {
for (ServiceDescriptor descriptor : descriptors) {
if (descriptor.getServiceClass() == serviceClass) {
return descriptor;
}
}
return null;
}
@SuppressWarnings("unchecked")
protected @NotNull T getInstance(@NotNull ServiceDescriptor descriptor) {
Map instances;
Lock lock;
if (descriptor.getLifetime() == ServiceLifetime.SINGLETON) {
instances = (parent != null ? parent : this).instances;
lock = (parent != null ? parent : this).lock;
} else if (descriptor.getLifetime() == ServiceLifetime.SCOPED) {
if (parent == null) {
throw new IllegalStateException(String.format("Cannot resolve '%s' from the root provider", descriptor.serviceClass));
}
instances = this.instances;
lock = this.lock;
} else {
instances = null;
lock = null;
}
if (instances != null && lock != null) {
Object preInstance = instances.get(descriptor);
if (preInstance != null) {
return (T) preInstance;
}
lock.lock();
Object postInstance = instances.get(descriptor);
if (postInstance != null) {
return (T) postInstance;
}
}
if (deque.contains(descriptor)) {
throw new IllegalStateException("Re-entrant detected");
}
deque.offerLast(descriptor);
try {
T instance = (T) descriptor.createInstance(this);
if (instances != null) {
instances.put(descriptor, instance);
}
if (instance instanceof AutoCloseable) {
closeables.add((AutoCloseable) instance);
}
return instance;
} catch (Throwable throwable) {
throw new IllegalStateException(throwable);
} finally {
deque.pollLast();
if (lock != null) {
lock.unlock();
}
}
}
@Override
public void close() throws Exception {
Exception ex = null;
for (AutoCloseable closeable : closeables) {
try {
closeable.close();
} catch (Throwable t) {
if (ex == null) {
ex = new Exception("Encountered an error while closing services");
}
ex.addSuppressed(t);
}
}
if (ex != null) {
throw ex;
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy