You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@metamodel.apache.org by ka...@apache.org on 2017/10/04 16:32:08 UTC

metamodel-membrane git commit: METAMODEL-1166: Fixed caching of tenant and data source context

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 <i....@gmail.com>
Authored: Wed Oct 4 09:31:26 2017 -0700
Committer: Kasper Sørensen <i....@gmail.com>
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": []