Repository: metamodel-membrane Updated Branches: refs/heads/master 6f929c29c -> 013c8dcbe
METAMODEL-1166: Fixed caching of tenant and data source context Closes #12 Project: http://git-wip-us.apache.org/repos/asf/metamodel-membrane/repo Commit: http://git-wip-us.apache.org/repos/asf/metamodel-membrane/commit/013c8dcb Tree: http://git-wip-us.apache.org/repos/asf/metamodel-membrane/tree/013c8dcb Diff: http://git-wip-us.apache.org/repos/asf/metamodel-membrane/diff/013c8dcb Branch: refs/heads/master Commit: 013c8dcbedd18745c200549271f6d69e096414a9 Parents: 6f929c2 Author: Kasper Sørensen <[email protected]> Authored: Wed Oct 4 09:31:26 2017 -0700 Committer: Kasper Sørensen <[email protected]> Committed: Wed Oct 4 09:31:38 2017 -0700 ---------------------------------------------------------------------- CHANGES.md | 1 + .../app/CachedDataSourceRegistryWrapper.java | 119 ------------------ .../membrane/app/DataSourceRegistry.java | 82 ------------- .../app/InMemoryDataSourceRegistry.java | 77 ------------ .../membrane/app/InMemoryTenantContext.java | 45 ------- .../membrane/app/InMemoryTenantRegistry.java | 79 ------------ .../metamodel/membrane/app/TenantContext.java | 31 ----- .../metamodel/membrane/app/TenantRegistry.java | 39 ------ .../app/registry/DataSourceRegistry.java | 82 +++++++++++++ .../membrane/app/registry/TenantContext.java | 31 +++++ .../membrane/app/registry/TenantRegistry.java | 39 ++++++ .../cache/CachedDataSourceRegistryWrapper.java | 120 +++++++++++++++++++ .../cache/CachedTenantRegistryWrapper.java | 113 +++++++++++++++++ .../file/FileBasedDataSourceRegistry.java | 2 +- .../registry/file/FileBasedTenantContext.java | 6 +- .../registry/file/FileBasedTenantRegistry.java | 4 +- .../memory/InMemoryDataSourceRegistry.java | 79 ++++++++++++ .../registry/memory/InMemoryTenantContext.java | 49 ++++++++ .../registry/memory/InMemoryTenantRegistry.java | 81 +++++++++++++ .../membrane/controllers/ColumnController.java | 4 +- .../controllers/DataSourceController.java | 6 +- .../membrane/controllers/QueryController.java | 4 +- .../membrane/controllers/SchemaController.java | 4 +- .../membrane/controllers/TableController.java | 4 +- .../controllers/TableDataController.java | 4 +- .../membrane/controllers/TenantController.java | 4 +- .../resources/context/application-context.xml | 10 +- .../controllers/DataSourceControllerTest.java | 4 +- .../TenantInteractionScenarioTest.java | 4 +- postman-tests/Membrane.postman_collection.json | 24 +++- 30 files changed, 646 insertions(+), 505 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/013c8dcb/CHANGES.md ---------------------------------------------------------------------- diff --git a/CHANGES.md b/CHANGES.md index 78c1e34..0d5c6c6 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,5 +3,6 @@ * [METAMODEL-1153] - Added endpoints for deleting data sources. * [METAMODEL-1147] - Implemented Swagger codegen to build model classes from swagger file. * [METAMODEL-1149] - File based registry for tenant and data source information. +* [METAMODEL-1166] - Caching of tenant and data source context information * [METAMODEL-1154] - Added update and delete capabilities in POST call to table data endpoint. * Established project source control, structure and build. http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/013c8dcb/core/src/main/java/org/apache/metamodel/membrane/app/CachedDataSourceRegistryWrapper.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/metamodel/membrane/app/CachedDataSourceRegistryWrapper.java b/core/src/main/java/org/apache/metamodel/membrane/app/CachedDataSourceRegistryWrapper.java deleted file mode 100644 index b7b5b1e..0000000 --- a/core/src/main/java/org/apache/metamodel/membrane/app/CachedDataSourceRegistryWrapper.java +++ /dev/null @@ -1,119 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 org.apache.metamodel.membrane.app; - -import java.util.List; -import java.util.concurrent.TimeUnit; - -import org.apache.metamodel.DataContext; -import org.apache.metamodel.MetaModelException; -import org.apache.metamodel.factory.DataContextProperties; -import org.apache.metamodel.membrane.app.exceptions.DataSourceAlreadyExistException; -import org.apache.metamodel.membrane.app.exceptions.NoSuchDataSourceException; -import org.apache.metamodel.util.FileHelper; - -import com.google.common.cache.CacheBuilder; -import com.google.common.cache.CacheLoader; -import com.google.common.cache.LoadingCache; -import com.google.common.cache.RemovalListener; -import com.google.common.cache.RemovalNotification; -import com.google.common.util.concurrent.UncheckedExecutionException; - -/** - * A wrapper that adds a cache around a {@link DataSourceRegistry} in order to prevent re-connecting all the time to the - * same data source. - */ -public class CachedDataSourceRegistryWrapper implements DataSourceRegistry { - - /** - * The default timeout (in seconds) before the cache evicts and closes the created {@link DataContext}s. - */ - public static final int DEFAULT_TIMEOUT_SECONDS = 60; - - private final DataSourceRegistry delegate; - private final LoadingCache<String, DataContext> loadingCache; - - public CachedDataSourceRegistryWrapper(final DataSourceRegistry delegate) { - this(delegate, DEFAULT_TIMEOUT_SECONDS, TimeUnit.SECONDS); - } - - public CachedDataSourceRegistryWrapper(final DataSourceRegistry delegate, final long cacheTimeout, - final TimeUnit cacheTimeoutUnit) { - this.delegate = delegate; - this.loadingCache = CacheBuilder.newBuilder().expireAfterAccess(cacheTimeout, cacheTimeoutUnit) - .removalListener(createRemovalListener()).build(createCacheLoader()); - } - - private RemovalListener<String, DataContext> createRemovalListener() { - return new RemovalListener<String, DataContext>() { - @Override - public void onRemoval(final RemovalNotification<String, DataContext> notification) { - final DataContext dataContext = notification.getValue(); - // some DataContexts are closeable - attempt closing it here - FileHelper.safeClose(dataContext); - } - }; - } - - private CacheLoader<String, DataContext> createCacheLoader() { - return new CacheLoader<String, DataContext>() { - @Override - public DataContext load(final String key) throws Exception { - return delegate.openDataContext(key); - } - }; - } - - @Override - public List<String> getDataSourceNames() { - return delegate.getDataSourceNames(); - } - - @Override - public String registerDataSource(final String dataSourceName, final DataContextProperties dataContextProperties) - throws DataSourceAlreadyExistException { - loadingCache.invalidate(dataSourceName); - return delegate.registerDataSource(dataSourceName, dataContextProperties); - } - - @Override - public DataContext openDataContext(final String dataSourceName) throws NoSuchDataSourceException { - try { - return loadingCache.getUnchecked(dataSourceName); - } catch (UncheckedExecutionException e) { - final Throwable cause = e.getCause(); - if (cause instanceof RuntimeException) { - throw (RuntimeException) cause; - } - throw new MetaModelException( - "Unexpected error happened while getting DataContext '" + dataSourceName + "' from cache", e); - } - } - - @Override - public void removeDataSource(String dataSourceName) throws NoSuchDataSourceException { - delegate.removeDataSource(dataSourceName); - loadingCache.invalidate(dataSourceName); - } - - @Override - public DataContext openDataContext(DataContextProperties properties) { - return delegate.openDataContext(properties); - } -} http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/013c8dcb/core/src/main/java/org/apache/metamodel/membrane/app/DataSourceRegistry.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/metamodel/membrane/app/DataSourceRegistry.java b/core/src/main/java/org/apache/metamodel/membrane/app/DataSourceRegistry.java deleted file mode 100644 index 1c5db17..0000000 --- a/core/src/main/java/org/apache/metamodel/membrane/app/DataSourceRegistry.java +++ /dev/null @@ -1,82 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 org.apache.metamodel.membrane.app; - -import java.util.List; - -import org.apache.metamodel.DataContext; -import org.apache.metamodel.UpdateableDataContext; -import org.apache.metamodel.factory.DataContextProperties; -import org.apache.metamodel.membrane.app.exceptions.DataSourceAlreadyExistException; -import org.apache.metamodel.membrane.app.exceptions.DataSourceNotUpdateableException; -import org.apache.metamodel.membrane.app.exceptions.NoSuchDataSourceException; - -/** - * Represents a user's/tenant's registry of {@link DataContext}s. - */ -public interface DataSourceRegistry { - - public List<String> getDataSourceNames(); - - /** - * - * @param dataSourceName - * @param dataContextProperties - * @return the identifier/name for the data source. - * @throws DataSourceAlreadyExistException - */ - public String registerDataSource(String dataSourceName, DataContextProperties dataContextProperties) - throws DataSourceAlreadyExistException; - - public void removeDataSource(String dataSourceName) throws NoSuchDataSourceException; - - /** - * Opens a {@link DataContext} that exists in the registry. - * - * @param dataSourceName - * @return - * @throws NoSuchDataSourceException - */ - public DataContext openDataContext(String dataSourceName) throws NoSuchDataSourceException; - - /** - * Opens a {@link DataContext} based on a set of {@link DataContextProperties}. This allows you to instantiate a - * data source without necesarily having registered it (yet). - * - * @param properties - * @return - */ - public DataContext openDataContext(DataContextProperties properties); - - /** - * Opens a {@link UpdateableDataContext} that exists in the registry. - * - * @param dataSourceName - * @return - * @throws DataSourceNotUpdateableException - */ - public default UpdateableDataContext openDataContextForUpdate(String dataSourceName) - throws DataSourceNotUpdateableException { - final DataContext dataContext = openDataContext(dataSourceName); - if (dataContext instanceof UpdateableDataContext) { - return (UpdateableDataContext) dataContext; - } - throw new DataSourceNotUpdateableException(dataSourceName); - } -} http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/013c8dcb/core/src/main/java/org/apache/metamodel/membrane/app/InMemoryDataSourceRegistry.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/metamodel/membrane/app/InMemoryDataSourceRegistry.java b/core/src/main/java/org/apache/metamodel/membrane/app/InMemoryDataSourceRegistry.java deleted file mode 100644 index 338ed86..0000000 --- a/core/src/main/java/org/apache/metamodel/membrane/app/InMemoryDataSourceRegistry.java +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 org.apache.metamodel.membrane.app; - -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.function.Supplier; -import java.util.stream.Collectors; - -import org.apache.metamodel.DataContext; -import org.apache.metamodel.factory.DataContextProperties; -import org.apache.metamodel.membrane.app.exceptions.DataSourceAlreadyExistException; -import org.apache.metamodel.membrane.app.exceptions.NoSuchDataSourceException; - -public class InMemoryDataSourceRegistry implements DataSourceRegistry { - - private final Map<String, Supplier<DataContext>> dataSources; - - public InMemoryDataSourceRegistry() { - dataSources = new LinkedHashMap<>(); - } - - @Override - public String registerDataSource(final String name, final DataContextProperties dataContextProperties) - throws DataSourceAlreadyExistException { - if (dataSources.containsKey(name)) { - throw new DataSourceAlreadyExistException(name); - } - - dataSources.put(name, new DataContextSupplier(name, dataContextProperties)); - return name; - } - - @Override - public List<String> getDataSourceNames() { - return dataSources.keySet().stream().collect(Collectors.toList()); - } - - @Override - public DataContext openDataContext(String name) { - final Supplier<DataContext> supplier = dataSources.get(name); - if (supplier == null) { - throw new NoSuchDataSourceException(name); - } - return supplier.get(); - } - - @Override - public void removeDataSource(String dataSourceName) throws NoSuchDataSourceException { - if (!dataSources.containsKey(dataSourceName)) { - throw new NoSuchDataSourceException(dataSourceName); - } - dataSources.remove(dataSourceName); - } - - public DataContext openDataContext(DataContextProperties properties) { - final DataContextSupplier supplier = new DataContextSupplier(null, properties); - return supplier.get(); - } -} http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/013c8dcb/core/src/main/java/org/apache/metamodel/membrane/app/InMemoryTenantContext.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/metamodel/membrane/app/InMemoryTenantContext.java b/core/src/main/java/org/apache/metamodel/membrane/app/InMemoryTenantContext.java deleted file mode 100644 index 3ecb7fe..0000000 --- a/core/src/main/java/org/apache/metamodel/membrane/app/InMemoryTenantContext.java +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 org.apache.metamodel.membrane.app; - -public class InMemoryTenantContext implements TenantContext { - - private final String tenantIdentifier; - private final DataSourceRegistry dataContextRegistry; - - public InMemoryTenantContext(String tenantIdentifier) { - this.tenantIdentifier = tenantIdentifier; - this.dataContextRegistry = new CachedDataSourceRegistryWrapper(new InMemoryDataSourceRegistry()); - } - - @Override - public String getTenantName() { - return tenantIdentifier; - } - - @Override - public DataSourceRegistry getDataSourceRegistry() { - return dataContextRegistry; - } - - @Override - public String toString() { - return "InMemoryTenantContext[" + tenantIdentifier + "]"; - } -} http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/013c8dcb/core/src/main/java/org/apache/metamodel/membrane/app/InMemoryTenantRegistry.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/metamodel/membrane/app/InMemoryTenantRegistry.java b/core/src/main/java/org/apache/metamodel/membrane/app/InMemoryTenantRegistry.java deleted file mode 100644 index 8665819..0000000 --- a/core/src/main/java/org/apache/metamodel/membrane/app/InMemoryTenantRegistry.java +++ /dev/null @@ -1,79 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 org.apache.metamodel.membrane.app; - -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import org.apache.metamodel.membrane.app.exceptions.NoSuchTenantException; -import org.apache.metamodel.membrane.app.exceptions.TenantAlreadyExistException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * In-memory {@link TenantRegistry}. This is not particularly - * production-friendly as it is non-persistent, but it is useful for demo - * purposes. - */ -public class InMemoryTenantRegistry implements TenantRegistry { - - private static final Logger logger = LoggerFactory.getLogger(InMemoryTenantRegistry.class); - private final Map<String, TenantContext> tenants; - - public InMemoryTenantRegistry() { - tenants = new LinkedHashMap<>(); - logger.info("Initialized!"); - } - - @Override - public List<String> getTenantIdentifiers() { - return tenants.keySet().stream().collect(Collectors.toList()); - } - - @Override - public TenantContext getTenantContext(String tenantIdentifier) { - final TenantContext tenant = tenants.get(tenantIdentifier); - if (tenant == null) { - throw new NoSuchTenantException(tenantIdentifier); - } - return tenant; - } - - @Override - public TenantContext createTenantContext(String tenantIdentifier) { - if (tenants.containsKey(tenantIdentifier)) { - throw new TenantAlreadyExistException(tenantIdentifier); - } - final InMemoryTenantContext tenantContext = new InMemoryTenantContext(tenantIdentifier); - tenants.put(tenantIdentifier, tenantContext); - logger.info("Created new tenant: {}", tenantContext); - return tenantContext; - } - - @Override - public void deleteTenantContext(String tenantIdentifier) { - final TenantContext removedTenant = tenants.remove(tenantIdentifier); - if (removedTenant == null) { - throw new NoSuchTenantException(tenantIdentifier); - } - } - -} http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/013c8dcb/core/src/main/java/org/apache/metamodel/membrane/app/TenantContext.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/metamodel/membrane/app/TenantContext.java b/core/src/main/java/org/apache/metamodel/membrane/app/TenantContext.java deleted file mode 100644 index 491859f..0000000 --- a/core/src/main/java/org/apache/metamodel/membrane/app/TenantContext.java +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 org.apache.metamodel.membrane.app; - -/** - * Represents a context-object containing all the information and services - * related to a particular tenant. - */ -public interface TenantContext { - - public String getTenantName(); - - public DataSourceRegistry getDataSourceRegistry(); - -} http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/013c8dcb/core/src/main/java/org/apache/metamodel/membrane/app/TenantRegistry.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/metamodel/membrane/app/TenantRegistry.java b/core/src/main/java/org/apache/metamodel/membrane/app/TenantRegistry.java deleted file mode 100644 index 6a32800..0000000 --- a/core/src/main/java/org/apache/metamodel/membrane/app/TenantRegistry.java +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 org.apache.metamodel.membrane.app; - -import java.util.List; - -import org.apache.metamodel.membrane.app.exceptions.NoSuchTenantException; -import org.apache.metamodel.membrane.app.exceptions.TenantAlreadyExistException; - -/** - * Represents the application's central registry of tenants - */ -public interface TenantRegistry { - - public List<String> getTenantIdentifiers(); - - public TenantContext getTenantContext(String tenantIdentifier) throws NoSuchTenantException; - - public TenantContext createTenantContext(String tenantIdentifier) throws IllegalArgumentException, - TenantAlreadyExistException; - - public void deleteTenantContext(String tenantIdentifier) throws NoSuchTenantException; -} http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/013c8dcb/core/src/main/java/org/apache/metamodel/membrane/app/registry/DataSourceRegistry.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/metamodel/membrane/app/registry/DataSourceRegistry.java b/core/src/main/java/org/apache/metamodel/membrane/app/registry/DataSourceRegistry.java new file mode 100644 index 0000000..00ec8e0 --- /dev/null +++ b/core/src/main/java/org/apache/metamodel/membrane/app/registry/DataSourceRegistry.java @@ -0,0 +1,82 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.metamodel.membrane.app.registry; + +import java.util.List; + +import org.apache.metamodel.DataContext; +import org.apache.metamodel.UpdateableDataContext; +import org.apache.metamodel.factory.DataContextProperties; +import org.apache.metamodel.membrane.app.exceptions.DataSourceAlreadyExistException; +import org.apache.metamodel.membrane.app.exceptions.DataSourceNotUpdateableException; +import org.apache.metamodel.membrane.app.exceptions.NoSuchDataSourceException; + +/** + * Represents a user's/tenant's registry of {@link DataContext}s. + */ +public interface DataSourceRegistry { + + public List<String> getDataSourceNames(); + + /** + * + * @param dataSourceName + * @param dataContextProperties + * @return the identifier/name for the data source. + * @throws DataSourceAlreadyExistException + */ + public String registerDataSource(String dataSourceName, DataContextProperties dataContextProperties) + throws DataSourceAlreadyExistException; + + public void removeDataSource(String dataSourceName) throws NoSuchDataSourceException; + + /** + * Opens a {@link DataContext} that exists in the registry. + * + * @param dataSourceName + * @return + * @throws NoSuchDataSourceException + */ + public DataContext openDataContext(String dataSourceName) throws NoSuchDataSourceException; + + /** + * Opens a {@link DataContext} based on a set of {@link DataContextProperties}. This allows you to instantiate a + * data source without necesarily having registered it (yet). + * + * @param properties + * @return + */ + public DataContext openDataContext(DataContextProperties properties); + + /** + * Opens a {@link UpdateableDataContext} that exists in the registry. + * + * @param dataSourceName + * @return + * @throws DataSourceNotUpdateableException + */ + public default UpdateableDataContext openDataContextForUpdate(String dataSourceName) + throws DataSourceNotUpdateableException { + final DataContext dataContext = openDataContext(dataSourceName); + if (dataContext instanceof UpdateableDataContext) { + return (UpdateableDataContext) dataContext; + } + throw new DataSourceNotUpdateableException(dataSourceName); + } +} http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/013c8dcb/core/src/main/java/org/apache/metamodel/membrane/app/registry/TenantContext.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/metamodel/membrane/app/registry/TenantContext.java b/core/src/main/java/org/apache/metamodel/membrane/app/registry/TenantContext.java new file mode 100644 index 0000000..4d7b470 --- /dev/null +++ b/core/src/main/java/org/apache/metamodel/membrane/app/registry/TenantContext.java @@ -0,0 +1,31 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.metamodel.membrane.app.registry; + +/** + * Represents a context-object containing all the information and services + * related to a particular tenant. + */ +public interface TenantContext { + + public String getTenantName(); + + public DataSourceRegistry getDataSourceRegistry(); + +} http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/013c8dcb/core/src/main/java/org/apache/metamodel/membrane/app/registry/TenantRegistry.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/metamodel/membrane/app/registry/TenantRegistry.java b/core/src/main/java/org/apache/metamodel/membrane/app/registry/TenantRegistry.java new file mode 100644 index 0000000..764fbc0 --- /dev/null +++ b/core/src/main/java/org/apache/metamodel/membrane/app/registry/TenantRegistry.java @@ -0,0 +1,39 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.metamodel.membrane.app.registry; + +import java.util.List; + +import org.apache.metamodel.membrane.app.exceptions.NoSuchTenantException; +import org.apache.metamodel.membrane.app.exceptions.TenantAlreadyExistException; + +/** + * Represents the application's central registry of tenants + */ +public interface TenantRegistry { + + public List<String> getTenantIdentifiers(); + + public TenantContext getTenantContext(String tenantIdentifier) throws NoSuchTenantException; + + public TenantContext createTenantContext(String tenantIdentifier) throws IllegalArgumentException, + TenantAlreadyExistException; + + public void deleteTenantContext(String tenantIdentifier) throws NoSuchTenantException; +} http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/013c8dcb/core/src/main/java/org/apache/metamodel/membrane/app/registry/cache/CachedDataSourceRegistryWrapper.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/metamodel/membrane/app/registry/cache/CachedDataSourceRegistryWrapper.java b/core/src/main/java/org/apache/metamodel/membrane/app/registry/cache/CachedDataSourceRegistryWrapper.java new file mode 100644 index 0000000..3a3a478 --- /dev/null +++ b/core/src/main/java/org/apache/metamodel/membrane/app/registry/cache/CachedDataSourceRegistryWrapper.java @@ -0,0 +1,120 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.metamodel.membrane.app.registry.cache; + +import java.util.List; +import java.util.concurrent.TimeUnit; + +import org.apache.metamodel.DataContext; +import org.apache.metamodel.MetaModelException; +import org.apache.metamodel.factory.DataContextProperties; +import org.apache.metamodel.membrane.app.exceptions.DataSourceAlreadyExistException; +import org.apache.metamodel.membrane.app.exceptions.NoSuchDataSourceException; +import org.apache.metamodel.membrane.app.registry.DataSourceRegistry; +import org.apache.metamodel.util.FileHelper; + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import com.google.common.cache.RemovalListener; +import com.google.common.cache.RemovalNotification; +import com.google.common.util.concurrent.UncheckedExecutionException; + +/** + * A wrapper that adds a cache around a {@link DataSourceRegistry} in order to prevent re-connecting all the time to the + * same data source. + */ +public class CachedDataSourceRegistryWrapper implements DataSourceRegistry { + + /** + * The default timeout (in seconds) before the cache evicts and closes the created {@link DataContext}s. + */ + public static final int DEFAULT_TIMEOUT_SECONDS = 60; + + private final DataSourceRegistry delegate; + private final LoadingCache<String, DataContext> loadingCache; + + public CachedDataSourceRegistryWrapper(final DataSourceRegistry delegate) { + this(delegate, DEFAULT_TIMEOUT_SECONDS, TimeUnit.SECONDS); + } + + public CachedDataSourceRegistryWrapper(final DataSourceRegistry delegate, final long cacheTimeout, + final TimeUnit cacheTimeoutUnit) { + this.delegate = delegate; + this.loadingCache = CacheBuilder.newBuilder().expireAfterAccess(cacheTimeout, cacheTimeoutUnit) + .removalListener(createRemovalListener()).build(createCacheLoader()); + } + + private RemovalListener<String, DataContext> createRemovalListener() { + return new RemovalListener<String, DataContext>() { + @Override + public void onRemoval(final RemovalNotification<String, DataContext> notification) { + final DataContext dataContext = notification.getValue(); + // some DataContexts are closeable - attempt closing it here + FileHelper.safeClose(dataContext); + } + }; + } + + private CacheLoader<String, DataContext> createCacheLoader() { + return new CacheLoader<String, DataContext>() { + @Override + public DataContext load(final String key) throws Exception { + return delegate.openDataContext(key); + } + }; + } + + @Override + public List<String> getDataSourceNames() { + return delegate.getDataSourceNames(); + } + + @Override + public String registerDataSource(final String dataSourceName, final DataContextProperties dataContextProperties) + throws DataSourceAlreadyExistException { + loadingCache.invalidate(dataSourceName); + return delegate.registerDataSource(dataSourceName, dataContextProperties); + } + + @Override + public DataContext openDataContext(final String dataSourceName) throws NoSuchDataSourceException { + try { + return loadingCache.getUnchecked(dataSourceName); + } catch (UncheckedExecutionException e) { + final Throwable cause = e.getCause(); + if (cause instanceof RuntimeException) { + throw (RuntimeException) cause; + } + throw new MetaModelException( + "Unexpected error happened while getting DataContext '" + dataSourceName + "' from cache", e); + } + } + + @Override + public void removeDataSource(String dataSourceName) throws NoSuchDataSourceException { + delegate.removeDataSource(dataSourceName); + loadingCache.invalidate(dataSourceName); + } + + @Override + public DataContext openDataContext(DataContextProperties properties) { + return delegate.openDataContext(properties); + } +} http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/013c8dcb/core/src/main/java/org/apache/metamodel/membrane/app/registry/cache/CachedTenantRegistryWrapper.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/metamodel/membrane/app/registry/cache/CachedTenantRegistryWrapper.java b/core/src/main/java/org/apache/metamodel/membrane/app/registry/cache/CachedTenantRegistryWrapper.java new file mode 100644 index 0000000..6ba500f --- /dev/null +++ b/core/src/main/java/org/apache/metamodel/membrane/app/registry/cache/CachedTenantRegistryWrapper.java @@ -0,0 +1,113 @@ +package org.apache.metamodel.membrane.app.registry.cache; + +import java.util.List; +import java.util.concurrent.TimeUnit; + +import org.apache.metamodel.MetaModelException; +import org.apache.metamodel.membrane.app.exceptions.NoSuchTenantException; +import org.apache.metamodel.membrane.app.exceptions.TenantAlreadyExistException; +import org.apache.metamodel.membrane.app.registry.TenantContext; +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +import org.apache.metamodel.membrane.app.registry.TenantRegistry; +import org.apache.metamodel.util.FileHelper; + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import com.google.common.cache.RemovalListener; +import com.google.common.cache.RemovalNotification; +import com.google.common.util.concurrent.UncheckedExecutionException; + +public class CachedTenantRegistryWrapper implements TenantRegistry { + + /** + * The default timeout (in seconds) before the cache evicts and closes the + * created {@link TenantContext}s. + */ + public static final int DEFAULT_TIMEOUT_SECONDS = 10 * 60; + + private final TenantRegistry delegate; + private final LoadingCache<String, TenantContext> loadingCache; + + public CachedTenantRegistryWrapper(TenantRegistry delegate) { + this(delegate, DEFAULT_TIMEOUT_SECONDS, TimeUnit.SECONDS); + } + + public CachedTenantRegistryWrapper(TenantRegistry delegate, final long cacheTimeout, + final TimeUnit cacheTimeoutUnit) { + this.delegate = delegate; + this.loadingCache = CacheBuilder.newBuilder().expireAfterAccess(cacheTimeout, cacheTimeoutUnit).removalListener( + createRemovalListener()).build(createCacheLoader()); + } + + private RemovalListener<String, TenantContext> createRemovalListener() { + return new RemovalListener<String, TenantContext>() { + @Override + public void onRemoval(final RemovalNotification<String, TenantContext> notification) { + final TenantContext tenantContext = notification.getValue(); + // TenantContexts could be closeable - attempt closing it here + FileHelper.safeClose(tenantContext); + } + }; + } + + private CacheLoader<String, TenantContext> createCacheLoader() { + return new CacheLoader<String, TenantContext>() { + @Override + public TenantContext load(final String key) throws Exception { + return delegate.getTenantContext(key); + } + }; + } + + @Override + public List<String> getTenantIdentifiers() { + return delegate.getTenantIdentifiers(); + } + + @Override + public TenantContext getTenantContext(String tenantIdentifier) throws NoSuchTenantException { + try { + return loadingCache.getUnchecked(tenantIdentifier); + } catch (UncheckedExecutionException e) { + final Throwable cause = e.getCause(); + if (cause instanceof RuntimeException) { + throw (RuntimeException) cause; + } + throw new MetaModelException("Unexpected error happened while getting TenantContext '" + tenantIdentifier + + "' from cache", e); + } + } + + @Override + public TenantContext createTenantContext(String tenantIdentifier) throws IllegalArgumentException, + TenantAlreadyExistException { + final TenantContext tenantContext = delegate.createTenantContext(tenantIdentifier); + loadingCache.put(tenantContext.getTenantName(), tenantContext); + return tenantContext; + } + + @Override + public void deleteTenantContext(String tenantIdentifier) throws NoSuchTenantException { + delegate.deleteTenantContext(tenantIdentifier); + loadingCache.invalidate(tenantIdentifier); + } + +} http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/013c8dcb/core/src/main/java/org/apache/metamodel/membrane/app/registry/file/FileBasedDataSourceRegistry.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/metamodel/membrane/app/registry/file/FileBasedDataSourceRegistry.java b/core/src/main/java/org/apache/metamodel/membrane/app/registry/file/FileBasedDataSourceRegistry.java index 1a7759d..60c77ae 100644 --- a/core/src/main/java/org/apache/metamodel/membrane/app/registry/file/FileBasedDataSourceRegistry.java +++ b/core/src/main/java/org/apache/metamodel/membrane/app/registry/file/FileBasedDataSourceRegistry.java @@ -29,10 +29,10 @@ import java.util.stream.Collectors; import org.apache.metamodel.DataContext; import org.apache.metamodel.factory.DataContextProperties; import org.apache.metamodel.membrane.app.DataContextSupplier; -import org.apache.metamodel.membrane.app.DataSourceRegistry; import org.apache.metamodel.membrane.app.config.JacksonConfig; import org.apache.metamodel.membrane.app.exceptions.DataSourceAlreadyExistException; import org.apache.metamodel.membrane.app.exceptions.NoSuchDataSourceException; +import org.apache.metamodel.membrane.app.registry.DataSourceRegistry; import org.apache.metamodel.membrane.controllers.model.RestDataSourceDefinition; import com.fasterxml.jackson.databind.ObjectMapper; http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/013c8dcb/core/src/main/java/org/apache/metamodel/membrane/app/registry/file/FileBasedTenantContext.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/metamodel/membrane/app/registry/file/FileBasedTenantContext.java b/core/src/main/java/org/apache/metamodel/membrane/app/registry/file/FileBasedTenantContext.java index b1bc48c..d830125 100644 --- a/core/src/main/java/org/apache/metamodel/membrane/app/registry/file/FileBasedTenantContext.java +++ b/core/src/main/java/org/apache/metamodel/membrane/app/registry/file/FileBasedTenantContext.java @@ -20,9 +20,9 @@ package org.apache.metamodel.membrane.app.registry.file; import java.io.File; -import org.apache.metamodel.membrane.app.CachedDataSourceRegistryWrapper; -import org.apache.metamodel.membrane.app.DataSourceRegistry; -import org.apache.metamodel.membrane.app.TenantContext; +import org.apache.metamodel.membrane.app.registry.DataSourceRegistry; +import org.apache.metamodel.membrane.app.registry.TenantContext; +import org.apache.metamodel.membrane.app.registry.cache.CachedDataSourceRegistryWrapper; class FileBasedTenantContext implements TenantContext { http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/013c8dcb/core/src/main/java/org/apache/metamodel/membrane/app/registry/file/FileBasedTenantRegistry.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/metamodel/membrane/app/registry/file/FileBasedTenantRegistry.java b/core/src/main/java/org/apache/metamodel/membrane/app/registry/file/FileBasedTenantRegistry.java index 6242038..94acdcd 100644 --- a/core/src/main/java/org/apache/metamodel/membrane/app/registry/file/FileBasedTenantRegistry.java +++ b/core/src/main/java/org/apache/metamodel/membrane/app/registry/file/FileBasedTenantRegistry.java @@ -30,10 +30,10 @@ import java.util.List; import java.util.stream.Collectors; import org.apache.commons.io.FileUtils; -import org.apache.metamodel.membrane.app.TenantContext; -import org.apache.metamodel.membrane.app.TenantRegistry; import org.apache.metamodel.membrane.app.exceptions.NoSuchTenantException; import org.apache.metamodel.membrane.app.exceptions.TenantAlreadyExistException; +import org.apache.metamodel.membrane.app.registry.TenantContext; +import org.apache.metamodel.membrane.app.registry.TenantRegistry; import com.google.common.base.Strings; http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/013c8dcb/core/src/main/java/org/apache/metamodel/membrane/app/registry/memory/InMemoryDataSourceRegistry.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/metamodel/membrane/app/registry/memory/InMemoryDataSourceRegistry.java b/core/src/main/java/org/apache/metamodel/membrane/app/registry/memory/InMemoryDataSourceRegistry.java new file mode 100644 index 0000000..a457ec0 --- /dev/null +++ b/core/src/main/java/org/apache/metamodel/membrane/app/registry/memory/InMemoryDataSourceRegistry.java @@ -0,0 +1,79 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.metamodel.membrane.app.registry.memory; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +import org.apache.metamodel.DataContext; +import org.apache.metamodel.factory.DataContextProperties; +import org.apache.metamodel.membrane.app.DataContextSupplier; +import org.apache.metamodel.membrane.app.exceptions.DataSourceAlreadyExistException; +import org.apache.metamodel.membrane.app.exceptions.NoSuchDataSourceException; +import org.apache.metamodel.membrane.app.registry.DataSourceRegistry; + +public class InMemoryDataSourceRegistry implements DataSourceRegistry { + + private final Map<String, Supplier<DataContext>> dataSources; + + public InMemoryDataSourceRegistry() { + dataSources = new LinkedHashMap<>(); + } + + @Override + public String registerDataSource(final String name, final DataContextProperties dataContextProperties) + throws DataSourceAlreadyExistException { + if (dataSources.containsKey(name)) { + throw new DataSourceAlreadyExistException(name); + } + + dataSources.put(name, new DataContextSupplier(name, dataContextProperties)); + return name; + } + + @Override + public List<String> getDataSourceNames() { + return dataSources.keySet().stream().collect(Collectors.toList()); + } + + @Override + public DataContext openDataContext(String name) { + final Supplier<DataContext> supplier = dataSources.get(name); + if (supplier == null) { + throw new NoSuchDataSourceException(name); + } + return supplier.get(); + } + + @Override + public void removeDataSource(String dataSourceName) throws NoSuchDataSourceException { + if (!dataSources.containsKey(dataSourceName)) { + throw new NoSuchDataSourceException(dataSourceName); + } + dataSources.remove(dataSourceName); + } + + public DataContext openDataContext(DataContextProperties properties) { + final DataContextSupplier supplier = new DataContextSupplier(null, properties); + return supplier.get(); + } +} http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/013c8dcb/core/src/main/java/org/apache/metamodel/membrane/app/registry/memory/InMemoryTenantContext.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/metamodel/membrane/app/registry/memory/InMemoryTenantContext.java b/core/src/main/java/org/apache/metamodel/membrane/app/registry/memory/InMemoryTenantContext.java new file mode 100644 index 0000000..08b2fa9 --- /dev/null +++ b/core/src/main/java/org/apache/metamodel/membrane/app/registry/memory/InMemoryTenantContext.java @@ -0,0 +1,49 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.metamodel.membrane.app.registry.memory; + +import org.apache.metamodel.membrane.app.registry.DataSourceRegistry; +import org.apache.metamodel.membrane.app.registry.TenantContext; +import org.apache.metamodel.membrane.app.registry.cache.CachedDataSourceRegistryWrapper; + +public class InMemoryTenantContext implements TenantContext { + + private final String tenantIdentifier; + private final DataSourceRegistry dataContextRegistry; + + public InMemoryTenantContext(String tenantIdentifier) { + this.tenantIdentifier = tenantIdentifier; + this.dataContextRegistry = new CachedDataSourceRegistryWrapper(new InMemoryDataSourceRegistry()); + } + + @Override + public String getTenantName() { + return tenantIdentifier; + } + + @Override + public DataSourceRegistry getDataSourceRegistry() { + return dataContextRegistry; + } + + @Override + public String toString() { + return "InMemoryTenantContext[" + tenantIdentifier + "]"; + } +} http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/013c8dcb/core/src/main/java/org/apache/metamodel/membrane/app/registry/memory/InMemoryTenantRegistry.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/metamodel/membrane/app/registry/memory/InMemoryTenantRegistry.java b/core/src/main/java/org/apache/metamodel/membrane/app/registry/memory/InMemoryTenantRegistry.java new file mode 100644 index 0000000..37586b1 --- /dev/null +++ b/core/src/main/java/org/apache/metamodel/membrane/app/registry/memory/InMemoryTenantRegistry.java @@ -0,0 +1,81 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.metamodel.membrane.app.registry.memory; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import org.apache.metamodel.membrane.app.exceptions.NoSuchTenantException; +import org.apache.metamodel.membrane.app.exceptions.TenantAlreadyExistException; +import org.apache.metamodel.membrane.app.registry.TenantContext; +import org.apache.metamodel.membrane.app.registry.TenantRegistry; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * In-memory {@link TenantRegistry}. This is not particularly + * production-friendly as it is non-persistent, but it is useful for demo + * purposes. + */ +public class InMemoryTenantRegistry implements TenantRegistry { + + private static final Logger logger = LoggerFactory.getLogger(InMemoryTenantRegistry.class); + private final Map<String, TenantContext> tenants; + + public InMemoryTenantRegistry() { + tenants = new LinkedHashMap<>(); + logger.info("Initialized!"); + } + + @Override + public List<String> getTenantIdentifiers() { + return tenants.keySet().stream().collect(Collectors.toList()); + } + + @Override + public TenantContext getTenantContext(String tenantIdentifier) { + final TenantContext tenant = tenants.get(tenantIdentifier); + if (tenant == null) { + throw new NoSuchTenantException(tenantIdentifier); + } + return tenant; + } + + @Override + public TenantContext createTenantContext(String tenantIdentifier) { + if (tenants.containsKey(tenantIdentifier)) { + throw new TenantAlreadyExistException(tenantIdentifier); + } + final InMemoryTenantContext tenantContext = new InMemoryTenantContext(tenantIdentifier); + tenants.put(tenantIdentifier, tenantContext); + logger.info("Created new tenant: {}", tenantContext); + return tenantContext; + } + + @Override + public void deleteTenantContext(String tenantIdentifier) { + final TenantContext removedTenant = tenants.remove(tenantIdentifier); + if (removedTenant == null) { + throw new NoSuchTenantException(tenantIdentifier); + } + } + +} http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/013c8dcb/core/src/main/java/org/apache/metamodel/membrane/controllers/ColumnController.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/metamodel/membrane/controllers/ColumnController.java b/core/src/main/java/org/apache/metamodel/membrane/controllers/ColumnController.java index 5e51a39..a0efb7b 100644 --- a/core/src/main/java/org/apache/metamodel/membrane/controllers/ColumnController.java +++ b/core/src/main/java/org/apache/metamodel/membrane/controllers/ColumnController.java @@ -20,8 +20,8 @@ package org.apache.metamodel.membrane.controllers; import org.apache.metamodel.DataContext; import org.apache.metamodel.membrane.app.DataContextTraverser; -import org.apache.metamodel.membrane.app.TenantContext; -import org.apache.metamodel.membrane.app.TenantRegistry; +import org.apache.metamodel.membrane.app.registry.TenantContext; +import org.apache.metamodel.membrane.app.registry.TenantRegistry; import org.apache.metamodel.membrane.swagger.model.GetColumnResponse; import org.apache.metamodel.membrane.swagger.model.GetColumnResponseMetadata; import org.apache.metamodel.schema.Column; http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/013c8dcb/core/src/main/java/org/apache/metamodel/membrane/controllers/DataSourceController.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/metamodel/membrane/controllers/DataSourceController.java b/core/src/main/java/org/apache/metamodel/membrane/controllers/DataSourceController.java index 0dea52e..21fe39f 100644 --- a/core/src/main/java/org/apache/metamodel/membrane/controllers/DataSourceController.java +++ b/core/src/main/java/org/apache/metamodel/membrane/controllers/DataSourceController.java @@ -30,10 +30,10 @@ import org.apache.metamodel.DataContext; import org.apache.metamodel.UpdateableDataContext; import org.apache.metamodel.factory.DataContextProperties; import org.apache.metamodel.factory.DataContextPropertiesImpl; -import org.apache.metamodel.membrane.app.DataSourceRegistry; -import org.apache.metamodel.membrane.app.TenantContext; -import org.apache.metamodel.membrane.app.TenantRegistry; import org.apache.metamodel.membrane.app.exceptions.InvalidDataSourceException; +import org.apache.metamodel.membrane.app.registry.DataSourceRegistry; +import org.apache.metamodel.membrane.app.registry.TenantContext; +import org.apache.metamodel.membrane.app.registry.TenantRegistry; import org.apache.metamodel.membrane.controllers.model.RestDataSourceDefinition; import org.apache.metamodel.membrane.swagger.model.DeleteDatasourceResponse; import org.apache.metamodel.membrane.swagger.model.GetDatasourceResponse; http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/013c8dcb/core/src/main/java/org/apache/metamodel/membrane/controllers/QueryController.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/metamodel/membrane/controllers/QueryController.java b/core/src/main/java/org/apache/metamodel/membrane/controllers/QueryController.java index 33a0567..7c8a90c 100644 --- a/core/src/main/java/org/apache/metamodel/membrane/controllers/QueryController.java +++ b/core/src/main/java/org/apache/metamodel/membrane/controllers/QueryController.java @@ -25,8 +25,8 @@ import java.util.stream.Collectors; import org.apache.metamodel.DataContext; import org.apache.metamodel.data.DataSet; -import org.apache.metamodel.membrane.app.TenantContext; -import org.apache.metamodel.membrane.app.TenantRegistry; +import org.apache.metamodel.membrane.app.registry.TenantContext; +import org.apache.metamodel.membrane.app.registry.TenantRegistry; import org.apache.metamodel.membrane.swagger.model.QueryResponse; import org.apache.metamodel.query.Query; import org.slf4j.Logger; http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/013c8dcb/core/src/main/java/org/apache/metamodel/membrane/controllers/SchemaController.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/metamodel/membrane/controllers/SchemaController.java b/core/src/main/java/org/apache/metamodel/membrane/controllers/SchemaController.java index 04d55e1..7be34b9 100644 --- a/core/src/main/java/org/apache/metamodel/membrane/controllers/SchemaController.java +++ b/core/src/main/java/org/apache/metamodel/membrane/controllers/SchemaController.java @@ -25,8 +25,8 @@ import javax.ws.rs.core.UriBuilder; import org.apache.metamodel.DataContext; import org.apache.metamodel.membrane.app.DataContextTraverser; -import org.apache.metamodel.membrane.app.TenantContext; -import org.apache.metamodel.membrane.app.TenantRegistry; +import org.apache.metamodel.membrane.app.registry.TenantContext; +import org.apache.metamodel.membrane.app.registry.TenantRegistry; import org.apache.metamodel.membrane.swagger.model.GetSchemaResponse; import org.apache.metamodel.membrane.swagger.model.GetSchemaResponseTables; import org.apache.metamodel.schema.Schema; http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/013c8dcb/core/src/main/java/org/apache/metamodel/membrane/controllers/TableController.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/metamodel/membrane/controllers/TableController.java b/core/src/main/java/org/apache/metamodel/membrane/controllers/TableController.java index af7decf..5da8bb0 100644 --- a/core/src/main/java/org/apache/metamodel/membrane/controllers/TableController.java +++ b/core/src/main/java/org/apache/metamodel/membrane/controllers/TableController.java @@ -25,8 +25,8 @@ import javax.ws.rs.core.UriBuilder; import org.apache.metamodel.DataContext; import org.apache.metamodel.membrane.app.DataContextTraverser; -import org.apache.metamodel.membrane.app.TenantContext; -import org.apache.metamodel.membrane.app.TenantRegistry; +import org.apache.metamodel.membrane.app.registry.TenantContext; +import org.apache.metamodel.membrane.app.registry.TenantRegistry; import org.apache.metamodel.membrane.swagger.model.GetTableResponse; import org.apache.metamodel.membrane.swagger.model.GetTableResponseColumns; import org.apache.metamodel.schema.Table; http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/013c8dcb/core/src/main/java/org/apache/metamodel/membrane/controllers/TableDataController.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/metamodel/membrane/controllers/TableDataController.java b/core/src/main/java/org/apache/metamodel/membrane/controllers/TableDataController.java index f31f690..42ed7ab 100644 --- a/core/src/main/java/org/apache/metamodel/membrane/controllers/TableDataController.java +++ b/core/src/main/java/org/apache/metamodel/membrane/controllers/TableDataController.java @@ -34,9 +34,9 @@ import org.apache.metamodel.data.WhereClauseBuilder; import org.apache.metamodel.delete.RowDeletionBuilder; import org.apache.metamodel.insert.RowInsertionBuilder; import org.apache.metamodel.membrane.app.DataContextTraverser; -import org.apache.metamodel.membrane.app.TenantContext; -import org.apache.metamodel.membrane.app.TenantRegistry; import org.apache.metamodel.membrane.app.config.JacksonConfig; +import org.apache.metamodel.membrane.app.registry.TenantContext; +import org.apache.metamodel.membrane.app.registry.TenantRegistry; import org.apache.metamodel.membrane.swagger.model.Operator; import org.apache.metamodel.membrane.swagger.model.PostDataRequest; import org.apache.metamodel.membrane.swagger.model.PostDataRequestDelete; http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/013c8dcb/core/src/main/java/org/apache/metamodel/membrane/controllers/TenantController.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/metamodel/membrane/controllers/TenantController.java b/core/src/main/java/org/apache/metamodel/membrane/controllers/TenantController.java index e94983b..d4c72e5 100644 --- a/core/src/main/java/org/apache/metamodel/membrane/controllers/TenantController.java +++ b/core/src/main/java/org/apache/metamodel/membrane/controllers/TenantController.java @@ -24,8 +24,8 @@ import java.util.stream.Collectors; import javax.ws.rs.core.UriBuilder; -import org.apache.metamodel.membrane.app.TenantContext; -import org.apache.metamodel.membrane.app.TenantRegistry; +import org.apache.metamodel.membrane.app.registry.TenantContext; +import org.apache.metamodel.membrane.app.registry.TenantRegistry; import org.apache.metamodel.membrane.swagger.model.DeleteTenantResponse; import org.apache.metamodel.membrane.swagger.model.GetTenantResponse; import org.apache.metamodel.membrane.swagger.model.GetTenantResponseDatasources; http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/013c8dcb/core/src/main/resources/context/application-context.xml ---------------------------------------------------------------------- diff --git a/core/src/main/resources/context/application-context.xml b/core/src/main/resources/context/application-context.xml index b5219ce..542d44d 100644 --- a/core/src/main/resources/context/application-context.xml +++ b/core/src/main/resources/context/application-context.xml @@ -33,8 +33,14 @@ under the License. <context:component-scan base-package="org.apache.metamodel.membrane.app" /> <bean id="tenantRegistry" - class="org.apache.metamodel.membrane.app.registry.file.FileBasedTenantRegistry"> - <constructor-arg name="directory" value="${DATA_DIRECTORY}" /> + class="org.apache.metamodel.membrane.app.registry.cache.CachedTenantRegistryWrapper"> + <constructor-arg name="delegate"> + <bean + class="org.apache.metamodel.membrane.app.registry.file.FileBasedTenantRegistry"> + <constructor-arg name="directory" value="${DATA_DIRECTORY}" /> + </bean> + </constructor-arg> </bean> + </beans> http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/013c8dcb/core/src/test/java/org/apache/metamodel/membrane/controllers/DataSourceControllerTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/metamodel/membrane/controllers/DataSourceControllerTest.java b/core/src/test/java/org/apache/metamodel/membrane/controllers/DataSourceControllerTest.java index 2a7e240..99a16f8 100644 --- a/core/src/test/java/org/apache/metamodel/membrane/controllers/DataSourceControllerTest.java +++ b/core/src/test/java/org/apache/metamodel/membrane/controllers/DataSourceControllerTest.java @@ -20,9 +20,9 @@ package org.apache.metamodel.membrane.controllers; import static org.junit.Assert.assertEquals; -import org.apache.metamodel.membrane.app.InMemoryTenantRegistry; -import org.apache.metamodel.membrane.app.TenantRegistry; import org.apache.metamodel.membrane.app.exceptions.InvalidDataSourceException; +import org.apache.metamodel.membrane.app.registry.TenantRegistry; +import org.apache.metamodel.membrane.app.registry.memory.InMemoryTenantRegistry; import org.apache.metamodel.membrane.controllers.model.RestDataSourceDefinition; import org.apache.metamodel.membrane.swagger.model.GetDatasourceResponse; import org.junit.Assert; http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/013c8dcb/core/src/test/java/org/apache/metamodel/membrane/controllers/TenantInteractionScenarioTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/metamodel/membrane/controllers/TenantInteractionScenarioTest.java b/core/src/test/java/org/apache/metamodel/membrane/controllers/TenantInteractionScenarioTest.java index 3e0575d..7071548 100644 --- a/core/src/test/java/org/apache/metamodel/membrane/controllers/TenantInteractionScenarioTest.java +++ b/core/src/test/java/org/apache/metamodel/membrane/controllers/TenantInteractionScenarioTest.java @@ -22,9 +22,9 @@ import static org.junit.Assert.assertEquals; import java.util.Map; -import org.apache.metamodel.membrane.app.InMemoryTenantRegistry; -import org.apache.metamodel.membrane.app.TenantRegistry; import org.apache.metamodel.membrane.app.config.JacksonConfig; +import org.apache.metamodel.membrane.app.registry.TenantRegistry; +import org.apache.metamodel.membrane.app.registry.memory.InMemoryTenantRegistry; import org.junit.Before; import org.junit.Test; import org.springframework.http.MediaType; http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/013c8dcb/postman-tests/Membrane.postman_collection.json ---------------------------------------------------------------------- diff --git a/postman-tests/Membrane.postman_collection.json b/postman-tests/Membrane.postman_collection.json index 62e816f..e249ead 100644 --- a/postman-tests/Membrane.postman_collection.json +++ b/postman-tests/Membrane.postman_collection.json @@ -2,7 +2,7 @@ "variables": [], "info": { "name": "Membrane", - "_postman_id": "1265d16a-b1fd-26a4-7ea5-d2e4015df97e", + "_postman_id": "fc875392-a36c-6ff7-46fb-53eab9cd3c3e", "description": "", "schema": "https://schema.getpostman.com/json/collection/v2.0.0/collection.json" }, @@ -27,7 +27,10 @@ "url": "{{baseUrl}}/swagger.json", "method": "GET", "header": [], - "body": {}, + "body": { + "mode": "raw", + "raw": "" + }, "description": "" }, "response": [] @@ -43,9 +46,12 @@ "tests[\"Status code is 200\"] = responseCode.code === 200;", "", "var jsonData = JSON.parse(responseBody);", - "tests[\"type is tenant\"] = jsonData.type === \"tenant\";", + "var isTenant = jsonData.type === \"tenant\";", + "tests[\"type is tenant\"] = isTenant;", "", - "postman.setGlobalVariable(\"membrane_tenant\", jsonData.name);", + "if (isTenant) {", + " postman.setGlobalVariable(\"membrane_tenant\", jsonData.name);", + "}", "" ] } @@ -89,7 +95,10 @@ "url": "{{baseUrl}}/{{membrane_tenant}}", "method": "GET", "header": [], - "body": {}, + "body": { + "mode": "raw", + "raw": "" + }, "description": "" }, "response": [] @@ -521,7 +530,10 @@ "url": "{{baseUrl}}/{{membrane_tenant}}/{{membrane_data_source}}", "method": "DELETE", "header": [], - "body": {}, + "body": { + "mode": "raw", + "raw": "" + }, "description": "" }, "response": []
