/* * Copyright (c) 2017 OBiBa. All rights reserved. * * This program and the accompanying materials * are made available under the terms of the GNU Public License v3.0. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.obiba.magma; import java.util.Map; import java.util.Set; import java.util.UUID; import javax.annotation.Nullable; import javax.validation.constraints.NotNull; import org.obiba.magma.support.Disposables; import org.obiba.magma.support.Initialisables; import org.obiba.magma.support.ValueTableReference; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.base.Strings; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Maps; import com.google.common.collect.Sets; public class DefaultDatasourceRegistry implements DatasourceRegistry, Disposable { private final Map<String, Datasource> datasources = Maps.newHashMap(); private final Map<String, DatasourceFactory> transientDatasourceFactories = Maps.newHashMap(); private final Map<String, Datasource> transientDatasources = Maps.newHashMap(); private final Set<Decorator<Datasource>> decorators = Sets.newHashSet(); @Override public void dispose() { for(Datasource ds : datasources.values()) { Disposables.silentlyDispose(ds); } for(Decorator<Datasource> decorator : decorators) { Disposables.silentlyDispose(decorator); } } @Override public ValueTableReference createReference(String reference) { return new ValueTableReference(reference); } @Override public Set<Datasource> getDatasources() { return ImmutableSet.copyOf(datasources.values()); } @Override public Datasource getDatasource(String name) throws NoSuchDatasourceException { Preconditions.checkArgument(!Strings.isNullOrEmpty(name), "datasource name cannot be null or empty"); Datasource datasource = datasources.get(name); if(datasource == null) { throw new NoSuchDatasourceException(name); } return datasource; } @Override public boolean hasDatasource(String name) { return datasources.containsKey(name); } @SuppressWarnings("ConstantConditions") @Override public void addDecorator(@NotNull Decorator<Datasource> decorator) { if(decorator == null) throw new MagmaRuntimeException("decorator cannot be null."); Initialisables.initialise(decorator); decorators.add(decorator); for(Datasource datasource : datasources.values()) { datasources.put(datasource.getName(), decorator.decorate(datasource)); } } @Override public Datasource addDatasource(Datasource datasource) { // Repeatedly added datasources are silently ignored. They cannot be added to the set more than once. if(!datasources.containsValue(datasource)) { Datasource existing = datasources.get(datasource.getName()); if(existing != null) { // Unique datasources with identical names cause exceptions. throw new DuplicateDatasourceNameException(existing, datasource); } Datasource decorated = decorateDatasource(datasource); Initialisables.initialise(decorated); datasources.put(decorated.getName(), decorated); return decorated; } return datasource; } private Datasource decorateDatasource(Datasource datasource) { Datasource decorated = datasource; for(Decorator<Datasource> decorator : decorators) { decorated = decorator.decorate(decorated); } return decorated; } private void releaseDatasource(Datasource datasource) { for(Decorator<Datasource> decorator : decorators) { decorator.release(datasource); } } @Override public Datasource addDatasource(DatasourceFactory factory) { Initialisables.initialise(factory); return addDatasource(factory.create()); } @Override public void removeDatasource(Datasource datasource) { releaseDatasource(datasource); datasources.remove(datasource.getName()); Disposables.silentlyDispose(datasource); } /** * Register a new transient datasource. * * @param factory * @return a unique identifier that can be used to obtain the registered factory */ @Override public String addTransientDatasource(DatasourceFactory factory) { String uid = randomTransientDatasourceName(); while(hasTransientDatasource(uid)) { uid = randomTransientDatasourceName(); } factory.setName(uid); Initialisables.initialise(factory); transientDatasourceFactories.put(factory.getName(), factory); return factory.getName(); } /** * Check if a transient datasource is registered with given identifier. * * @param uid * @return true when uid is associated with a DatasourceFactory instance */ @Override public boolean hasTransientDatasource(String uid) { return transientDatasourceFactories.containsKey(uid); } /** * Dispose & remove the transient datasource & factory identified by uid (ignore if none is found). * * @param uid */ @Override public void removeTransientDatasource(@Nullable String uid) { DatasourceFactory factory = transientDatasourceFactories.get(uid); if(factory != null) { Disposables.silentlyDispose(factory); transientDatasourceFactories.remove(uid); } Datasource datasource = transientDatasources.get(uid); if(datasource != null) { releaseDatasource(datasource); Disposables.silentlyDispose(datasource); transientDatasources.remove(uid); } } /** * Returns a new (initialized) instance of Datasource obtained by calling DatasourceFactory.create() associated with * uid. * * @param uid * @return datasource item */ @Override public Datasource getTransientDatasourceInstance(String uid) { Preconditions.checkArgument(!Strings.isNullOrEmpty(uid), "uid cannot be null or empty"); DatasourceFactory factory = transientDatasourceFactories.get(uid); if(factory == null) { throw new NoSuchDatasourceException(uid); } Datasource datasource = transientDatasources.get(uid); if(datasource == null) { datasource = factory.create(); Initialisables.initialise(datasource); datasource = decorateDatasource(datasource); transientDatasources.put(uid, datasource); } return datasource; } /** * Generate a random name. * * @return */ @VisibleForTesting String randomTransientDatasourceName() { return UUID.randomUUID().toString(); } }