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": []

Reply via email to