Repository: metamodel-membrane
Updated Branches:
  refs/heads/master 8d6636a89 -> d5419cb34


http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/6492107d/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
new file mode 100644
index 0000000..82f568d
--- /dev/null
+++ 
b/core/src/main/java/org/apache/metamodel/membrane/controllers/TableController.java
@@ -0,0 +1,85 @@
+/**
+ * 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.controllers;
+
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+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.controllers.model.RestLink;
+import org.apache.metamodel.schema.Table;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping(value = { 
"/{tenant}/{dataContext}/schemas/{schema}/tables/{table}",
+        "/{tenant}/{dataContext}/s/{schema}/t/{table}" }, produces = 
MediaType.APPLICATION_JSON_VALUE)
+public class TableController {
+
+    private final TenantRegistry tenantRegistry;
+
+    @Autowired
+    public TableController(TenantRegistry tenantRegistry) {
+        this.tenantRegistry = tenantRegistry;
+    }
+
+    @RequestMapping(method = RequestMethod.GET)
+    @ResponseBody
+    public Map<String, Object> get(@PathVariable("tenant") String tenantId,
+            @PathVariable("dataContext") String dataSourceName, 
@PathVariable("schema") String schemaId,
+            @PathVariable("table") String tableId) {
+        final TenantContext tenantContext = 
tenantRegistry.getTenantContext(tenantId);
+        final DataContext dataContext = 
tenantContext.getDataSourceRegistry().openDataContext(dataSourceName);
+        
+        final DataContextTraverser traverser = new 
DataContextTraverser(dataContext);
+
+        final Table table = traverser.getTable(schemaId, tableId);
+
+        final String tenantName = tenantContext.getTenantName();
+        final UriBuilder uriBuilder = 
UriBuilder.fromPath("/{tenant}/{dataContext}/s/{schema}/t/{table}/c/{column}");
+
+        final String tableName = table.getName();
+        final String schemaName = table.getSchema().getName();
+        final List<RestLink> columnsLinks = 
Arrays.stream(table.getColumnNames()).map(c -> new RestLink(String.valueOf(
+                c), uriBuilder.build(tenantName, dataSourceName, schemaName, 
tableName, c))).collect(Collectors
+                        .toList());
+
+        final Map<String, Object> map = new LinkedHashMap<>();
+        map.put("type", "table");
+        map.put("name", tableName);
+        map.put("schema", schemaName);
+        map.put("datasource", dataSourceName);
+        map.put("tenant", tenantName);
+        map.put("columns", columnsLinks);
+        return map;
+    }
+}

http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/6492107d/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
new file mode 100644
index 0000000..e906556
--- /dev/null
+++ 
b/core/src/main/java/org/apache/metamodel/membrane/controllers/TableDataController.java
@@ -0,0 +1,115 @@
+/**
+ * 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.controllers;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.metamodel.DataContext;
+import org.apache.metamodel.UpdateCallback;
+import org.apache.metamodel.UpdateScript;
+import org.apache.metamodel.UpdateSummary;
+import org.apache.metamodel.UpdateableDataContext;
+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.query.Query;
+import org.apache.metamodel.schema.Table;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping(value = { 
"/{tenant}/{dataContext}/schemas/{schema}/tables/{table}/data",
+        "/{tenant}/{dataContext}/s/{schema}/t/{table}/d" }, produces = 
MediaType.APPLICATION_JSON_VALUE)
+public class TableDataController {
+
+    private final TenantRegistry tenantRegistry;
+
+    @Autowired
+    public TableDataController(TenantRegistry tenantRegistry) {
+        this.tenantRegistry = tenantRegistry;
+    }
+
+    @RequestMapping(method = RequestMethod.GET)
+    @ResponseBody
+    public Map<String, Object> get(@PathVariable("tenant") String tenantId,
+            @PathVariable("dataContext") String dataSourceName, 
@PathVariable("schema") String schemaId,
+            @PathVariable("table") String tableId, @RequestParam(value = 
"offset", required = false) Integer offset,
+            @RequestParam(value = "limit", required = false) Integer limit) {
+        final TenantContext tenantContext = 
tenantRegistry.getTenantContext(tenantId);
+        final DataContext dataContext = 
tenantContext.getDataSourceRegistry().openDataContext(dataSourceName);
+        
+        final DataContextTraverser traverser = new 
DataContextTraverser(dataContext);
+
+        final Table table = traverser.getTable(schemaId, tableId);
+
+        final Query query = 
dataContext.query().from(table).selectAll().toQuery();
+
+        return QueryController.executeQuery(dataContext, query, offset, limit);
+    }
+
+    @RequestMapping(method = RequestMethod.POST)
+    @ResponseBody
+    public Map<String, Object> post(@PathVariable("tenant") String tenantId,
+            @PathVariable("dataContext") String dataSourceName, 
@PathVariable("schema") String schemaId,
+            @PathVariable("table") String tableId, @RequestBody final 
List<Map<String, Object>> inputRecords) {
+
+        final TenantContext tenantContext = 
tenantRegistry.getTenantContext(tenantId);
+        final UpdateableDataContext dataContext = 
tenantContext.getDataSourceRegistry().openDataContextForUpdate(dataSourceName);
+
+        final DataContextTraverser traverser = new 
DataContextTraverser(dataContext);
+
+        final Table table = traverser.getTable(schemaId, tableId);
+
+        final UpdateSummary result = dataContext.executeUpdate(new 
UpdateScript() {
+            @Override
+            public void run(UpdateCallback callback) {
+                for (Map<String, Object> inputMap : inputRecords) {
+                    final RowInsertionBuilder insert = 
callback.insertInto(table);
+                    for (Entry<String, Object> entry : inputMap.entrySet()) {
+                        insert.value(entry.getKey(), entry.getValue());
+                    }
+                    insert.execute();
+                }
+            }
+        });
+
+        final Map<String, Object> response = new LinkedHashMap<>();
+        response.put("status", "ok");
+
+        if (result.getInsertedRows().isPresent()) {
+            response.put("inserted-rows", result.getInsertedRows().get());
+        }
+        if (result.getGeneratedKeys().isPresent()) {
+            response.put("generated-keys", result.getGeneratedKeys().get());
+        }
+
+        return response;
+    }
+}

http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/6492107d/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
new file mode 100644
index 0000000..c4af271
--- /dev/null
+++ 
b/core/src/main/java/org/apache/metamodel/membrane/controllers/TenantController.java
@@ -0,0 +1,94 @@
+/**
+ * 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.controllers;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+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.controllers.model.RestLink;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping(value = "/{tenant}", produces = 
MediaType.APPLICATION_JSON_VALUE)
+public class TenantController {
+
+    private final TenantRegistry tenantRegistry;
+
+    @Autowired
+    public TenantController(TenantRegistry tenantRegistry) {
+        this.tenantRegistry = tenantRegistry;
+    }
+
+    @RequestMapping(method = RequestMethod.GET)
+    @ResponseBody
+    public Map<String, Object> getTenant(@PathVariable("tenant") String 
tenantName) {
+        final TenantContext tenantContext = 
tenantRegistry.getTenantContext(tenantName);
+        final String tenantNameNormalized = tenantContext.getTenantName();
+
+        final UriBuilder uriBuilder = 
UriBuilder.fromPath("/{tenant}/{datasource}");
+
+        final List<String> dataContextIdentifiers = 
tenantContext.getDataSourceRegistry().getDataSourceNames();
+        final List<RestLink> dataSourceLinks = 
dataContextIdentifiers.stream().map(s -> new RestLink(s, uriBuilder
+                .build(tenantNameNormalized, s))).collect(Collectors.toList());
+
+        final Map<String, Object> map = new LinkedHashMap<>();
+        map.put("type", "tenant");
+        map.put("name", tenantNameNormalized);
+        map.put("datasources", dataSourceLinks);
+        return map;
+    }
+
+    @RequestMapping(method = RequestMethod.PUT)
+    @ResponseBody
+    public Map<String, Object> putTenant(@PathVariable("tenant") String 
tenantName) {
+        final TenantContext tenantContext = 
tenantRegistry.createTenantContext(tenantName);
+        final String tenantIdentifier = tenantContext.getTenantName();
+
+        final Map<String, Object> map = new LinkedHashMap<>();
+        map.put("type", "tenant");
+        map.put("name", tenantIdentifier);
+
+        return map;
+    }
+
+    @RequestMapping(method = RequestMethod.DELETE)
+    @ResponseBody
+    public Map<String, Object> deleteTenant(@PathVariable("tenant") String 
tenantName) {
+        tenantRegistry.deleteTenantContext(tenantName);
+
+        final Map<String, Object> map = new LinkedHashMap<>();
+        map.put("type", "tenant");
+        map.put("name", tenantName);
+        map.put("deleted", true);
+
+        return map;
+    }
+}

http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/6492107d/core/src/main/java/org/apache/metamodel/membrane/controllers/model/RestDataSourceDefinition.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/metamodel/membrane/controllers/model/RestDataSourceDefinition.java
 
b/core/src/main/java/org/apache/metamodel/membrane/controllers/model/RestDataSourceDefinition.java
new file mode 100644
index 0000000..500427e
--- /dev/null
+++ 
b/core/src/main/java/org/apache/metamodel/membrane/controllers/model/RestDataSourceDefinition.java
@@ -0,0 +1,55 @@
+/**
+ * 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.controllers.model;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.validation.constraints.NotNull;
+
+import org.apache.metamodel.membrane.app.DataSourceDefinition;
+
+import com.fasterxml.jackson.annotation.JsonAnyGetter;
+import com.fasterxml.jackson.annotation.JsonAnySetter;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class RestDataSourceDefinition implements DataSourceDefinition {
+
+    private final Map<String, Object> properties = new HashMap<>();
+
+    @JsonProperty(value = "type", required = true)
+    @NotNull
+    private String type;
+
+    @Override
+    public String getType() {
+        return type;
+    }
+
+    @JsonAnyGetter
+    @Override
+    public Map<String, Object> getProperties() {
+        return properties;
+    }
+
+    @JsonAnySetter
+    public void set(String name, Object value) {
+        properties.put(name, value);
+    }
+}

http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/6492107d/core/src/main/java/org/apache/metamodel/membrane/controllers/model/RestErrorResponse.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/metamodel/membrane/controllers/model/RestErrorResponse.java
 
b/core/src/main/java/org/apache/metamodel/membrane/controllers/model/RestErrorResponse.java
new file mode 100644
index 0000000..ad2ce2a
--- /dev/null
+++ 
b/core/src/main/java/org/apache/metamodel/membrane/controllers/model/RestErrorResponse.java
@@ -0,0 +1,80 @@
+/**
+ * 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.controllers.model;
+
+import java.util.Map;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * Represents the JSON object that is returned when an error occurs
+ */
+public class RestErrorResponse {
+
+    @JsonProperty("code")
+    private int code;
+
+    @JsonProperty("message")
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    private String message;
+
+    @JsonProperty("additional_details")
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    private Map<String, Object> additionalDetails;
+
+    public RestErrorResponse(int code, String message) {
+        this(code, message, null);
+    }
+
+    public RestErrorResponse(int code, String message, Map<String, Object> 
additionalDetails) {
+        this.code = code;
+        this.message = message;
+        this.additionalDetails = additionalDetails;
+    }
+
+    public RestErrorResponse() {
+        this(-1, null, null);
+    }
+
+    public int getCode() {
+        return code;
+    }
+
+    public void setCode(int code) {
+        this.code = code;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+
+    public void setMessage(String message) {
+        this.message = message;
+    }
+
+    public void setAdditionalDetails(Map<String, Object> additionalDetails) {
+        this.additionalDetails = additionalDetails;
+    }
+
+    public Map<String, Object> getAdditionalDetails() {
+        return additionalDetails;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/6492107d/core/src/main/java/org/apache/metamodel/membrane/controllers/model/RestLink.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/metamodel/membrane/controllers/model/RestLink.java
 
b/core/src/main/java/org/apache/metamodel/membrane/controllers/model/RestLink.java
new file mode 100644
index 0000000..01fb76d
--- /dev/null
+++ 
b/core/src/main/java/org/apache/metamodel/membrane/controllers/model/RestLink.java
@@ -0,0 +1,60 @@
+/**
+ * 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.controllers.model;
+
+import java.io.Serializable;
+import java.net.URI;
+
+/**
+ * Represents a hyper-link to a service (typically provided in the REST
+ * responses)
+ */
+public class RestLink implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    private String name;
+
+    private URI uri;
+
+    public RestLink() {
+    }
+
+    public RestLink(String name, URI uri) {
+        this();
+        this.name = name;
+        this.uri = uri;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public URI getUri() {
+        return uri;
+    }
+
+    public void setUri(URI uri) {
+        this.uri = uri;
+    }
+}

http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/6492107d/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
new file mode 100644
index 0000000..e28e2b0
--- /dev/null
+++ b/core/src/main/resources/context/application-context.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans";
+       xmlns:security="http://www.springframework.org/schema/security";
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xmlns:p="http://www.springframework.org/schema/p";
+       xmlns:context="http://www.springframework.org/schema/context";
+       xmlns:mvc="http://www.springframework.org/schema/mvc";
+       xsi:schemaLocation="http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans.xsd
+       http://www.springframework.org/schema/mvc 
http://www.springframework.org/schema/mvc/spring-mvc.xsd
+       http://www.springframework.org/schema/security 
http://www.springframework.org/schema/security/spring-security.xsd
+                               http://www.springframework.org/schema/context 
http://www.springframework.org/schema/context/spring-context.xsd";>
+
+       <context:component-scan 
base-package="org.apache.metamodel.membrane.app" />
+
+       <bean id="tenantRegistry" 
class="org.apache.metamodel.membrane.app.InMemoryTenantRegistry" />
+
+</beans>

http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/6492107d/core/src/main/resources/context/dispatcher-servlet.xml
----------------------------------------------------------------------
diff --git a/core/src/main/resources/context/dispatcher-servlet.xml 
b/core/src/main/resources/context/dispatcher-servlet.xml
new file mode 100644
index 0000000..17a4be7
--- /dev/null
+++ b/core/src/main/resources/context/dispatcher-servlet.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans";
+       xmlns:security="http://www.springframework.org/schema/security";
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xmlns:p="http://www.springframework.org/schema/p";
+       xmlns:context="http://www.springframework.org/schema/context";
+       xmlns:mvc="http://www.springframework.org/schema/mvc";
+       xsi:schemaLocation="http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans.xsd
+       http://www.springframework.org/schema/mvc 
http://www.springframework.org/schema/mvc/spring-mvc.xsd
+       http://www.springframework.org/schema/security 
http://www.springframework.org/schema/security/spring-security.xsd
+                               http://www.springframework.org/schema/context 
http://www.springframework.org/schema/context/spring-context.xsd";>
+
+       <context:component-scan 
base-package="org.apache.metamodel.membrane.controllers" />
+
+       <mvc:annotation-driven>
+               <mvc:message-converters>
+                       <ref bean="jsonMessageConverter" />
+               </mvc:message-converters>
+       </mvc:annotation-driven>
+
+       <mvc:resources location="/swagger-ui/" mapping="/swagger-ui/**" 
order="-1001" />
+       
+       <!-- Message converters -->
+       <bean id="jsonMessageConverter"
+               
class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
+               <property name="supportedMediaTypes">
+                       <list>
+                               <value>application/json;charset=UTF-8</value>
+                               <value>application/json</value>
+                       </list>
+               </property>
+       </bean>
+
+       <bean
+               
class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
+               <property name="messageConverters">
+                       <list>
+                               <ref bean="jsonMessageConverter" />
+                       </list>
+               </property>
+       </bean>
+</beans>

http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/6492107d/core/src/main/resources/logback.xml
----------------------------------------------------------------------
diff --git a/core/src/main/resources/logback.xml 
b/core/src/main/resources/logback.xml
new file mode 100644
index 0000000..8ba8596
--- /dev/null
+++ b/core/src/main/resources/logback.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<configuration>
+
+       <appender name="consoleAppender" 
class="ch.qos.logback.core.ConsoleAppender">
+               <encoder 
class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+                       <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level 
%logger{36} - %msg%n</pattern>
+               </encoder>
+       </appender>
+
+       <logger name="org.apache.metamodel" level="info" />
+
+       <root level="warn">
+               <appender-ref ref="consoleAppender" />
+       </root>
+</configuration>

http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/6492107d/core/src/main/resources/swagger.yaml
----------------------------------------------------------------------
diff --git a/core/src/main/resources/swagger.yaml 
b/core/src/main/resources/swagger.yaml
new file mode 100644
index 0000000..dc5ef93
--- /dev/null
+++ b/core/src/main/resources/swagger.yaml
@@ -0,0 +1,538 @@
+# 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.
+swagger: '2.0'
+info:
+  title: Apache MetaModel Membrane
+  description: Data Federation as a RESTful service.
+  version: "1.0"
+  contact:
+    name: Apache MetaModel
+    url: http://metamodel.apache.org
+  license:
+    name: Apache License, version 2.0
+    url: http://www.apache.org/licenses/LICENSE-2.0
+consumes:
+  - application/json
+produces:
+  - application/json
+paths:
+  /:
+    get:
+      summary: Hello Membrane
+      description: An endpoint that provides a confirmation that the system is 
operational
+      responses:
+        200:
+          description: The system is operational
+          schema:
+            type: object
+            properties:
+              ping:
+                type: string
+                description: Should return 'pong!' when the system is 
operational
+              application:
+                type: string
+                description: The name of the application running (Apache 
MetaModel Membrane)
+              version:
+                type: string
+                description: The version of the application running
+              server-time:
+                type: object
+                properties:
+                  timestamp:
+                    type: integer
+                    format: int64
+                    description: The server-time in timestamp format (millis 
since 1st of January 1970)
+                  iso8601:
+                    type: string
+                    description: The server-time in ISO-8601 format
+              canonical-hostname:
+                type: string
+                description: The canonical hostname of the server
+  /{tenant}:
+    parameters:
+      - name: tenant
+        in: path
+        type: string
+        description: The tenant name
+        required: true
+    put:
+      summary: Create new tenant
+      responses:
+        200:
+          description: Tenant created
+          schema:
+            properties:
+              type:
+                type: string
+                description: The type of entity (tenant)
+              name:
+                type: string
+                description: The tenant name/identifier
+        409:
+          description: Tenant already exist
+          schema:
+            $ref: "#/definitions/error"
+    get:
+      summary: Get tenant information
+      description: Provides basic information about a tenant of the system
+      responses:
+        404:
+          description: Tenant not found
+          schema:
+            $ref: "#/definitions/error"
+        200:
+          description: Tenant found
+          schema:
+            type: object
+            properties:
+              type:
+                type: string
+                description: The type of entity (tenant)
+              name:
+                type: string
+                description: The tenant name/identifier
+              datasources:
+                type: array
+                items:
+                  type: object
+                  properties:
+                    name:
+                      type: string
+                      description: The name of the datasource
+                    uri:
+                      type: string
+                      format: uri
+                      description: A link to the datasource information
+    delete:
+      summary: Delete tenant
+      description: Deletes a tenant from the system
+      responses:
+        200:
+          description: Tenant deleted
+          schema:
+            type: object
+            properties:
+              type:
+                type: string
+                description: The type of entity (tenant)
+              name:
+                type: string
+                description: The tenant name/identifier
+        404:
+          description: Tenant not found
+          schema:
+            $ref: "#/definitions/error"
+  /{tenant}/{datasource}:
+    parameters:
+      - name: tenant
+        in: path
+        type: string
+        description: The tenant name
+        required: true
+      - name: datasource
+        in: path
+        type: string
+        description: The datasource name
+        required: true
+    get:
+      responses:
+        200:
+          description: Datasource found
+          schema:
+            type: object
+            properties:
+              type:
+                type: string
+                description: The type of entity (datasource)
+              name:
+                type: string
+                description: The datasource name
+              tenant:
+                type: string
+                description: The tenant name
+              updateable:
+                type: boolean
+                description: Is this datasource updateable?
+              query_uri:
+                type: string
+                description: A link to the query endpoint for this datasource
+                format: uri
+              schemas:
+                type: array
+                description: The schemas of this datasource
+                items:
+                  type: object
+                  properties:
+                    name:
+                      type: string
+                      description: The schema name
+                    uri:
+                      type: string
+                      description: A link to the schema information
+                      format: uri
+        404:
+          description: Datasource not found
+          schema:
+            $ref: "#/definitions/error"
+    put:
+      parameters:
+        - name: inputData
+          in: body
+          description: The definition of the datasource using properties. The 
same properties as normally applied in MetaModel factories (e.g. 'type', 
'resource', 'url', 'driver-class' ,'hostname', 'port', 'catalog', 'database', 
'username', 'port', 'table-defs') are used here.
+          required: true
+          schema:
+            type: object
+      responses:
+        200:
+          description: Datasource created
+  /{tenant}/{datasource}/q:
+    parameters:
+      - name: tenant
+        in: path
+        type: string
+        description: The tenant name
+        required: true
+      - name: datasource
+        in: path
+        type: string
+        description: The datasource name
+        required: true
+    get:
+      description: Executes a query on the datasource
+      parameters:
+      - name: sql
+        in: query
+        type: string
+        description: The MetaModel-flavoured SQL query to execute
+        required: true
+      - name: offset
+        in: query
+        type: string
+        description: An offset / first-row flag to set on the query
+        required: false
+      - name: limit
+        in: query
+        type: string
+        description: A limit / max-rows flag to set on the query
+        required: false
+      responses:
+        200:
+          description: Query executed
+          schema:
+            $ref: "#/definitions/queryResponse"
+        400:
+          description: Failure while parsing query
+          schema:
+            $ref: "#/definitions/error"
+        404:
+          description: Datasource not found
+          schema:
+            $ref: "#/definitions/error"
+        500:
+          description: Failure while executing query
+          schema:
+            $ref: "#/definitions/error"
+  /{tenant}/{datasource}/s/{schema}:
+    parameters:
+      - name: tenant
+        in: path
+        type: string
+        description: The tenant name
+        required: true
+      - name: datasource
+        in: path
+        type: string
+        description: The datasource name
+        required: true
+      - name: schema
+        in: path
+        type: string
+        description: The schema name
+        required: true
+    get:
+      responses:
+        200:
+          description: Schema found
+          schema:
+            type: object
+            properties:
+              type:
+                type: string
+                description: The type of entity (schema)
+              name:
+                type: string
+                description: The schema name
+              datasource:
+                type: string
+                description: The datasource name
+              tables:
+                type: array
+                description: The names of the schema's tables
+                items:
+                  type: object
+                  properties:
+                    name:
+                      type: string
+                      description: The table name
+                    uri:
+                      type: string
+                      description: A link to the table information
+                      format: uri
+        404:
+          description: Schema not found
+          schema:
+            $ref: "#/definitions/error"
+  /{tenant}/{datasource}/s/{schema}/t/{table}:
+    parameters:
+      - name: tenant
+        in: path
+        type: string
+        description: The tenant name
+        required: true
+      - name: datasource
+        in: path
+        type: string
+        description: The datasource name
+        required: true
+      - name: schema
+        in: path
+        type: string
+        description: The schema name
+        required: true
+      - name: table
+        in: path
+        type: string
+        description: The table name
+        required: true
+    get:
+      responses:
+        200:
+          description: Table found
+          schema:
+            type: object
+            properties:
+              type:
+                type: string
+                description: The type of entity (table)
+              name:
+                type: string
+                description: The table name
+              schema:
+                type: string
+                description: The schema name
+              datasource:
+                type: string
+                description: The datasource name
+              tenant:
+                type: string
+                description: The tenant name
+              columns:
+                type: array
+                description: The names of the table's columns
+                items:
+                  type: object
+                  properties:
+                    name:
+                      type: string
+                      description: The column name
+                    uri:
+                      type: string
+                      description: A link to the column information
+                      format: uri
+        404:
+          description: Table not found
+          schema:
+            $ref: "#/definitions/error"
+  /{tenant}/{datasource}/s/{schema}/t/{table}/d:
+    parameters:
+      - name: tenant
+        in: path
+        type: string
+        description: The tenant name
+        required: true
+      - name: datasource
+        in: path
+        type: string
+        description: The datasource name
+        required: true
+      - name: schema
+        in: path
+        type: string
+        description: The schema name
+        required: true
+      - name: table
+        in: path
+        type: string
+        description: The table name
+        required: true
+    get:
+      description: Gets the data of the table
+      responses:
+        200:
+          description: Query executed
+          schema:
+            $ref: "#/definitions/queryResponse"
+        400:
+          description: Failure while parsing query
+          schema:
+            $ref: "#/definitions/error"
+        404:
+          description: Table not found
+          schema:
+            $ref: "#/definitions/error"
+        500:
+          description: Failure while executing query
+          schema:
+            $ref: "#/definitions/error"
+    post:
+      description: Inserts data to the table
+      parameters:
+        - name: inputData
+          in: body
+          description: The data to insert
+          required: true
+          schema:
+            type: array
+            items:
+              description: A record to insert where each key is expected to 
match a column name and each value is the value to put.
+              type: object
+      responses:
+        200:
+          description: Data inserted
+          #TODO
+        404:
+          description: Table not found
+          schema:
+            $ref: "#/definitions/error"
+  /{tenant}/{datasource}/s/{schema}/t/{table}/c/{column}:
+    parameters:
+      - name: tenant
+        in: path
+        type: string
+        description: The tenant name
+        required: true
+      - name: datasource
+        in: path
+        type: string
+        description: The datasource name
+        required: true
+      - name: schema
+        in: path
+        type: string
+        description: The schema name
+        required: true
+      - name: table
+        in: path
+        type: string
+        description: The table name
+        required: true
+      - name: column
+        in: path
+        type: string
+        description: The column name
+        required: true
+    get:
+      description: Gets information about a column
+      responses:
+        200:
+          description: Query executed
+          schema:
+            type: object
+            properties:
+              type:
+                type: string
+                description: The type of entity (column)
+              name:
+                type: string
+                description: The column name
+              table:
+                type: string
+                description: The table name
+              schema:
+                type: string
+                description: The schema name
+              datasource:
+                type: string
+                description: The datasource name
+              tenant:
+                type: string
+                description: The tenant name
+              metadata:
+                type: object
+                description: Metadata about the column
+                properties:
+                  number:
+                    type: integer
+                    description: The column number (0-based)
+                  size:
+                    type: integer
+                    description: The column size
+                  nullable:
+                    type: boolean
+                    description: Is the column nullable?
+                  primary-key:
+                    type: boolean
+                    description: Is the column a primary key?
+                  indexed:
+                    type: boolean
+                    description: Is the column indexed?
+                  column-type:
+                    type: string
+                    description: The column type (as interpreted/adapted by 
Apache MetaModel)
+                  native-type:
+                    type: string
+                    description: The native column type (as defined by the 
datasource itself)
+                  remarks:
+                    type: string
+                    description: Any remarks on the column
+        404:
+          description: Column not found
+          schema:
+            $ref: "#/definitions/error"
+definitions:
+  queryResponse:
+    description: Represents the result of a query - a dataset
+    type: object
+    properties:
+      type:
+        type: string
+        description: The type of entity (dataset)
+      headers:
+        type: array
+        description: The dataset header names
+        items:
+          type: string
+      data:
+        type: array
+        description: The actual data returned by the query
+        items:
+          type: array
+          items:
+            type: object
+  error:
+    description: Elaborates the error that occurred
+    type: object
+    properties:
+      code:
+        type: integer
+        description: The HTTP status code
+      message:
+        type: string
+        description: A humanly readable error message
+      additional_details:
+        type: object
+        description: Any auxilary details to further elaborate the error

http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/6492107d/core/src/test/java/org/apache/metamodel/membrane/controllers/RootInformationControllerTest.java
----------------------------------------------------------------------
diff --git 
a/core/src/test/java/org/apache/metamodel/membrane/controllers/RootInformationControllerTest.java
 
b/core/src/test/java/org/apache/metamodel/membrane/controllers/RootInformationControllerTest.java
new file mode 100644
index 0000000..1b5fbbf
--- /dev/null
+++ 
b/core/src/test/java/org/apache/metamodel/membrane/controllers/RootInformationControllerTest.java
@@ -0,0 +1,62 @@
+/**
+ * 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.controllers;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.Map;
+
+import org.apache.metamodel.membrane.controllers.RootInformationController;
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.http.MediaType;
+import org.springframework.mock.web.MockServletContext;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.MvcResult;
+import 
org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
+import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
+import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class RootInformationControllerTest {
+
+    private MockMvc mockMvc;
+
+    @Before
+    public void init() {
+        final RootInformationController controller = new 
RootInformationController();
+        controller.servletContext = new MockServletContext();
+        mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
+    }
+
+    @Test
+    public void testGet() throws Exception {
+        final MockHttpServletRequestBuilder request = 
MockMvcRequestBuilders.get("/").contentType(
+                MediaType.APPLICATION_JSON);
+
+        final MvcResult result = 
mockMvc.perform(request).andExpect(MockMvcResultMatchers.status().is(200)).andReturn();
+
+        final String content = result.getResponse().getContentAsString();
+
+        final Map<?, ?> map = new ObjectMapper().readValue(content, Map.class);
+        assertEquals("pong!", map.get("ping"));
+    }
+}

http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/6492107d/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
new file mode 100644
index 0000000..4c01e53
--- /dev/null
+++ 
b/core/src/test/java/org/apache/metamodel/membrane/controllers/TenantInteractionScenarioTest.java
@@ -0,0 +1,202 @@
+/**
+ * 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.controllers;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import java.util.Map;
+
+import org.apache.metamodel.membrane.app.InMemoryTenantRegistry;
+import org.apache.metamodel.membrane.app.TenantRegistry;
+import org.apache.metamodel.membrane.controllers.ColumnController;
+import org.apache.metamodel.membrane.controllers.DataSourceController;
+import org.apache.metamodel.membrane.controllers.QueryController;
+import org.apache.metamodel.membrane.controllers.SchemaController;
+import org.apache.metamodel.membrane.controllers.TableController;
+import org.apache.metamodel.membrane.controllers.TableDataController;
+import org.apache.metamodel.membrane.controllers.TenantController;
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.http.MediaType;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.MvcResult;
+import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
+import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class TenantInteractionScenarioTest {
+
+    private MockMvc mockMvc;
+
+    @Before
+    public void init() {
+        TenantRegistry tenantRegistry = new InMemoryTenantRegistry();
+        TenantController tenantController = new 
TenantController(tenantRegistry);
+        DataSourceController dataContextController = new 
DataSourceController(tenantRegistry);
+        SchemaController schemaController = new 
SchemaController(tenantRegistry);
+        TableController tableController = new TableController(tenantRegistry);
+        ColumnController columnController = new 
ColumnController(tenantRegistry);
+        QueryController queryController = new QueryController(tenantRegistry);
+        TableDataController tableDataController = new 
TableDataController(tenantRegistry);
+
+        mockMvc = MockMvcBuilders.standaloneSetup(tenantController, 
dataContextController, schemaController,
+                tableController, columnController, queryController, 
tableDataController).build();
+    }
+
+    @Test
+    public void testScenario() throws Exception {
+        // create tenant
+        {
+            final MvcResult result = 
mockMvc.perform(MockMvcRequestBuilders.put("/tenant1").contentType(
+                    
MediaType.APPLICATION_JSON)).andExpect(MockMvcResultMatchers.status().isOk()).andReturn();
+
+            final String content = result.getResponse().getContentAsString();
+            final Map<?, ?> map = new ObjectMapper().readValue(content, 
Map.class);
+            assertEquals("tenant", map.get("type"));
+            assertEquals("tenant1", map.get("name"));
+            assertNull(map.get("datasources"));
+        }
+
+        // create datasource
+        {
+            final MvcResult result = 
mockMvc.perform(MockMvcRequestBuilders.put("/tenant1/mydata").content(
+                    "{'type':'pojo','table-defs':'hello_world (greeting 
VARCHAR, who VARCHAR); foo (bar INTEGER, baz DATE);'}"
+                            .replace('\'', 
'"')).contentType(MediaType.APPLICATION_JSON)).andExpect(
+                                    
MockMvcResultMatchers.status().isOk()).andReturn();
+
+            final String content = result.getResponse().getContentAsString();
+            final Map<?, ?> map = new ObjectMapper().readValue(content, 
Map.class);
+            assertEquals("datasource", map.get("type"));
+            assertEquals("mydata", map.get("name"));
+            assertEquals(
+                    "[{name=information_schema, 
uri=/tenant1/mydata/s/information_schema}, {name=mydata, 
uri=/tenant1/mydata/s/mydata}]",
+                    map.get("schemas").toString());
+        }
+
+        // explore tenant - now with a datasource
+        {
+            final MvcResult result = 
mockMvc.perform(MockMvcRequestBuilders.get("/tenant1")).andExpect(
+                    MockMvcResultMatchers.status().isOk()).andReturn();
+
+            final String content = result.getResponse().getContentAsString();
+            final Map<?, ?> map = new ObjectMapper().readValue(content, 
Map.class);
+            assertEquals("tenant", map.get("type"));
+            assertEquals("tenant1", map.get("name"));
+            assertEquals("[{name=mydata, uri=/tenant1/mydata}]", 
map.get("datasources").toString());
+        }
+
+        // explore schema
+        {
+            final MvcResult result = 
mockMvc.perform(MockMvcRequestBuilders.get("/tenant1/mydata/s/mydata")).andExpect(
+                    MockMvcResultMatchers.status().isOk()).andReturn();
+
+            final String content = result.getResponse().getContentAsString();
+            final Map<?, ?> map = new ObjectMapper().readValue(content, 
Map.class);
+            assertEquals("schema", map.get("type"));
+            assertEquals("mydata", map.get("name"));
+
+            assertEquals(
+                    "[{name=foo, uri=/tenant1/mydata/s/mydata/t/foo}, 
{name=hello_world, uri=/tenant1/mydata/s/mydata/t/hello_world}]",
+                    map.get("tables").toString());
+        }
+
+        // explore table
+        {
+            final MvcResult result = 
mockMvc.perform(MockMvcRequestBuilders.get("/tenant1/mydata/s/mydata/t/foo"))
+                    
.andExpect(MockMvcResultMatchers.status().isOk()).andReturn();
+
+            final String content = result.getResponse().getContentAsString();
+            final Map<?, ?> map = new ObjectMapper().readValue(content, 
Map.class);
+            assertEquals("table", map.get("type"));
+            assertEquals("foo", map.get("name"));
+
+            assertEquals("[{name=bar, 
uri=/tenant1/mydata/s/mydata/t/foo/c/bar}, "
+                    + "{name=baz, uri=/tenant1/mydata/s/mydata/t/foo/c/baz}]", 
map.get("columns").toString());
+        }
+
+        // explore column
+        {
+            final MvcResult result = 
mockMvc.perform(MockMvcRequestBuilders.get("/tenant1/mydata/s/mydata/t/foo/c/bar"))
+                    
.andExpect(MockMvcResultMatchers.status().isOk()).andReturn();
+
+            final String content = result.getResponse().getContentAsString();
+            final Map<?, ?> map = new ObjectMapper().readValue(content, 
Map.class);
+            assertEquals("column", map.get("type"));
+            assertEquals("bar", map.get("name"));
+
+            assertEquals(
+                    "{number=0, size=null, nullable=true, primary-key=false, 
indexed=false, column-type=INTEGER, native-type=null, remarks=null}",
+                    map.get("metadata").toString());
+        }
+
+        // query metadata from information_schema
+        {
+            final MvcResult result = 
mockMvc.perform(MockMvcRequestBuilders.get("/tenant1/mydata/q?sql={sql}",
+                    "SELECT name, table FROM 
information_schema.columns")).andExpect(MockMvcResultMatchers.status()
+                            .isOk()).andReturn();
+
+            final String content = result.getResponse().getContentAsString();
+            final Map<?, ?> map = new ObjectMapper().readValue(content, 
Map.class);
+            assertEquals("dataset", map.get("type"));
+            assertEquals("[columns.name, columns.table]", 
map.get("header").toString());
+            assertEquals("[[bar, foo], [baz, foo], [greeting, hello_world], 
[who, hello_world]]", map.get("data")
+                    .toString());
+        }
+
+        // insert into table (x2)
+        {
+            final MvcResult result = 
mockMvc.perform(MockMvcRequestBuilders.post(
+                    
"/tenant1/mydata/s/mydata/t/hello_world/d").content("[{'greeting':'Howdy','who':'MetaModel'}]"
+                            .replace('\'', 
'"')).contentType(MediaType.APPLICATION_JSON)).andExpect(
+                                    
MockMvcResultMatchers.status().isOk()).andReturn();
+
+            final String content = result.getResponse().getContentAsString();
+            final Map<?, ?> map = new ObjectMapper().readValue(content, 
Map.class);
+            assertEquals("{status=ok}", map.toString());
+        }
+        {
+            final MvcResult result = 
mockMvc.perform(MockMvcRequestBuilders.post(
+                    
"/tenant1/mydata/s/mydata/t/hello_world/d").content("[{'greeting':'Hi','who':'Apache'}]"
+                            .replace('\'', 
'"')).contentType(MediaType.APPLICATION_JSON)).andExpect(
+                                    
MockMvcResultMatchers.status().isOk()).andReturn();
+
+            final String content = result.getResponse().getContentAsString();
+            final Map<?, ?> map = new ObjectMapper().readValue(content, 
Map.class);
+            assertEquals("{status=ok}", map.toString());
+        }
+        
+        // query the actual data
+        // query metadata from information_schema
+        {
+            final MvcResult result = 
mockMvc.perform(MockMvcRequestBuilders.get("/tenant1/mydata/q?sql={sql}",
+                    "SELECT greeting, who AS who_is_it FROM 
hello_world")).andExpect(MockMvcResultMatchers.status()
+                            .isOk()).andReturn();
+
+            final String content = result.getResponse().getContentAsString();
+            final Map<?, ?> map = new ObjectMapper().readValue(content, 
Map.class);
+            assertEquals("dataset", map.get("type"));
+            assertEquals("[hello_world.greeting, hello_world.who AS 
who_is_it]", map.get("header").toString());
+            assertEquals("[[Howdy, MetaModel], [Hi, Apache]]", map.get("data")
+                    .toString());
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/6492107d/docker-compose.yml
----------------------------------------------------------------------
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000..c87a2bb
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,26 @@
+# 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.
+version: '2'
+services:
+  metamodel-membrane:
+    container_name: metamodel-membrane
+    image: metamodel-membrane
+    build: undertow
+    ports:
+    - "8080:8080"
+    environment:
+    - MEMBRANE_HTTP_PORT=8080

http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/6492107d/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index b8dc59f..b5d51d5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,25 +1,32 @@
 <?xml version="1.0" encoding="UTF-8" ?>
-<!-- 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. -->
+<!--
+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.
+-->
 <project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/maven-v4_0_0.xsd";>
        <modelVersion>4.0.0</modelVersion>
        <properties>
-               
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-               <metamodel.version>5.0-RC2</metamodel.version>
+               <metamodel.version>5.0-SNAPSHOT</metamodel.version>
        </properties>
        <parent>
                <groupId>org.apache.metamodel</groupId>
                <artifactId>MetaModel</artifactId>
-               <version>5.0-RC2</version>
+               <version>5.0-SNAPSHOT</version>
        </parent>
        <scm>
                
<url>https://git-wip-us.apache.org/repos/asf?p=metamodel-membrane.git</url>
@@ -36,25 +43,34 @@
        <inceptionYear>2017</inceptionYear>
        <packaging>pom</packaging>
        <modules>
+               <module>core</module>
+               <module>war</module>
+               <module>undertow</module>
        </modules>
 
        <dependencyManagement>
                <dependencies>
                        <dependency>
-                               <groupId>org.apache.metanmodel</groupId>
+                               <groupId>org.apache.metamodel</groupId>
                                <artifactId>MetaModel-core</artifactId>
                                <version>${metamodel.version}</version>
                        </dependency>
                        <dependency>
-                               <groupId>org.apache.metanmodel</groupId>
+                               <groupId>org.apache.metamodel</groupId>
                                <artifactId>MetaModel-spring</artifactId>
                                <version>${metamodel.version}</version>
                        </dependency>
                        <dependency>
-                               <groupId>org.apache.metanmodel</groupId>
+                               <groupId>org.apache.metamodel</groupId>
                                <artifactId>MetaModel-full</artifactId>
                                <version>${metamodel.version}</version>
                        </dependency>
+                       <dependency>
+                               <groupId>javax.servlet</groupId>
+                               <artifactId>javax.servlet-api</artifactId>
+                               <version>3.1.0</version>
+                               <scope>provided</scope>
+                       </dependency>
                </dependencies>
        </dependencyManagement>
 </project>

http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/6492107d/undertow/Dockerfile
----------------------------------------------------------------------
diff --git a/undertow/Dockerfile b/undertow/Dockerfile
new file mode 100644
index 0000000..3e14546
--- /dev/null
+++ b/undertow/Dockerfile
@@ -0,0 +1,22 @@
+# 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.
+
+FROM openjdk:8-jre-alpine
+
+COPY target/membrane-undertow-server.jar membrane-undertow-server.jar
+
+CMD java -server -jar membrane-undertow-server.jar

http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/6492107d/undertow/pom.xml
----------------------------------------------------------------------
diff --git a/undertow/pom.xml b/undertow/pom.xml
new file mode 100644
index 0000000..81c2600
--- /dev/null
+++ b/undertow/pom.xml
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+       xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+       <parent>
+               <groupId>org.apache.metamodel.membrane</groupId>
+               <artifactId>Membrane-parent</artifactId>
+               <version>0.1-SNAPSHOT</version>
+       </parent>
+       <modelVersion>4.0.0</modelVersion>
+       <artifactId>Membrane-undertow</artifactId>
+       <packaging>jar</packaging>
+
+       <build>
+               <finalName>membrane-undertow-server</finalName>
+
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-shade-plugin</artifactId>
+                               <version>2.4.3</version>
+                               <executions>
+                                       <execution>
+                                               <phase>package</phase>
+                                               <goals>
+                                                       <goal>shade</goal>
+                                               </goals>
+                                               <configuration>
+                                                       
<createDependencyReducedPom>false</createDependencyReducedPom>
+                                                       <transformers>
+                                                               <transformer
+                                                                       
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
+                                                                       
<mainClass>org.apache.metamodel.membrane.server.WebServer</mainClass>
+                                                               </transformer>
+                                                               <transformer
+                                                                       
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+                                                                       
<resource>META-INF/spring.handlers</resource>
+                                                               </transformer>
+                                                               <transformer
+                                                                       
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+                                                                       
<resource>META-INF/spring.schemas</resource>
+                                                               </transformer>
+                                                       </transformers>
+                                               </configuration>
+                                       </execution>
+                               </executions>
+                       </plugin>
+               </plugins>
+       </build>
+
+       <dependencies>
+               <dependency>
+                       <groupId>org.apache.metamodel.membrane</groupId>
+                       <artifactId>Membrane-core</artifactId>
+                       <version>${project.version}</version>
+               </dependency>
+               <dependency>
+                       <groupId>io.undertow</groupId>
+                       <artifactId>undertow-servlet</artifactId>
+                       <version>1.3.23.Final</version>
+               </dependency>
+       </dependencies>
+</project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/6492107d/undertow/src/main/docker/swagger-ui/index.html
----------------------------------------------------------------------
diff --git a/undertow/src/main/docker/swagger-ui/index.html 
b/undertow/src/main/docker/swagger-ui/index.html
new file mode 100644
index 0000000..fae3de9
--- /dev/null
+++ b/undertow/src/main/docker/swagger-ui/index.html
@@ -0,0 +1,112 @@
+<!DOCTYPE html>
+<!--
+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.
+-->
+<html>
+<head>
+  <meta charset="UTF-8">
+  <title>Swagger UI</title>
+  <link rel="icon" type="image/png" href="images/favicon-32x32.png" 
sizes="32x32" />
+  <link rel="icon" type="image/png" href="images/favicon-16x16.png" 
sizes="16x16" />
+  <link href='css/typography.css' media='screen' rel='stylesheet' 
type='text/css'/>
+  <link href='css/reset.css' media='screen' rel='stylesheet' type='text/css'/>
+  <link href='css/screen.css' media='screen' rel='stylesheet' type='text/css'/>
+  <link href='css/reset.css' media='print' rel='stylesheet' type='text/css'/>
+  <link href='css/print.css' media='print' rel='stylesheet' type='text/css'/>
+
+  <script src='lib/object-assign-pollyfill.js' type='text/javascript'></script>
+  <script src='lib/jquery-1.8.0.min.js' type='text/javascript'></script>
+  <script src='lib/jquery.slideto.min.js' type='text/javascript'></script>
+  <script src='lib/jquery.wiggle.min.js' type='text/javascript'></script>
+  <script src='lib/jquery.ba-bbq.min.js' type='text/javascript'></script>
+  <script src='lib/handlebars-2.0.0.js' type='text/javascript'></script>
+  <script src='lib/lodash.min.js' type='text/javascript'></script>
+  <script src='lib/backbone-min.js' type='text/javascript'></script>
+  <script src='swagger-ui.js' type='text/javascript'></script>
+  <script src='lib/highlight.9.1.0.pack.js' type='text/javascript'></script>
+  <script src='lib/highlight.9.1.0.pack_extended.js' 
type='text/javascript'></script>
+  <script src='lib/jsoneditor.min.js' type='text/javascript'></script>
+  <script src='lib/marked.js' type='text/javascript'></script>
+  <script src='lib/swagger-oauth.js' type='text/javascript'></script>
+
+  <!-- Some basic translations -->
+  <!-- <script src='lang/translator.js' type='text/javascript'></script> -->
+  <!-- <script src='lang/ru.js' type='text/javascript'></script> -->
+  <!-- <script src='lang/en.js' type='text/javascript'></script> -->
+
+  <script type="text/javascript">
+    $(function () {
+      var url = window.location.search.match(/url=([^&]+)/);
+      if (url && url.length > 1) {
+        url = decodeURIComponent(url[1]);
+      } else {
+        url = "../../swagger.json";
+      }
+
+      hljs.configure({
+        highlightSizeThreshold: 5000
+      });
+
+      // Pre load translate...
+      if(window.SwaggerTranslator) {
+        window.SwaggerTranslator.translate();
+      }
+      window.swaggerUi = new SwaggerUi({
+        url: url,
+        dom_id: "swagger-ui-container",
+        supportedSubmitMethods: ['get', 'post', 'put', 'delete', 'patch'],
+        onComplete: function(swaggerApi, swaggerUi){
+          if(typeof initOAuth == "function") {
+            initOAuth({
+              clientId: "your-client-id",
+              clientSecret: "your-client-secret-if-required",
+              realm: "your-realms",
+              appName: "your-app-name",
+              scopeSeparator: ",",
+              additionalQueryStringParams: {}
+            });
+          }
+
+          if(window.SwaggerTranslator) {
+            window.SwaggerTranslator.translate();
+          }
+        },
+        onFailure: function(data) {
+          log("Unable to Load SwaggerUI");
+        },
+        docExpansion: "none",
+        jsonEditor: false,
+        defaultModelRendering: 'schema',
+        showRequestHeaders: false
+      });
+
+      window.swaggerUi.load();
+
+      function log() {
+        if ('console' in window) {
+          console.log.apply(console, arguments);
+        }
+      }
+  });
+  </script>
+</head>
+
+<body class="swagger-section">
+<div id="swagger-ui-container" class="swagger-ui-wrap"></div>
+</body>
+</html>

http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/6492107d/undertow/src/main/java/org/apache/metamodel/membrane/server/WebServer.java
----------------------------------------------------------------------
diff --git 
a/undertow/src/main/java/org/apache/metamodel/membrane/server/WebServer.java 
b/undertow/src/main/java/org/apache/metamodel/membrane/server/WebServer.java
new file mode 100644
index 0000000..1643bd8
--- /dev/null
+++ b/undertow/src/main/java/org/apache/metamodel/membrane/server/WebServer.java
@@ -0,0 +1,92 @@
+/**
+ * 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.server;
+
+import java.io.File;
+
+import javax.servlet.ServletException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.context.ContextLoaderListener;
+import org.springframework.web.context.request.RequestContextListener;
+import org.springframework.web.filter.CharacterEncodingFilter;
+import org.springframework.web.servlet.DispatcherServlet;
+
+import com.google.common.base.Strings;
+
+import io.undertow.Undertow;
+import io.undertow.server.handlers.resource.FileResourceManager;
+import io.undertow.servlet.Servlets;
+import io.undertow.servlet.api.DeploymentInfo;
+import io.undertow.servlet.api.DeploymentManager;
+
+/**
+ * Defines the main class that starts the Undertow-based web server.
+ */
+public class WebServer {
+
+    private static final Logger logger = 
LoggerFactory.getLogger(WebServer.class);
+
+    private static final int DEFAULT_PORT = 8080;
+
+    public static void main(final String[] args) throws Exception {
+        final String portEnv = System.getenv("MEMBRANE_HTTP_PORT");
+        final int port = Strings.isNullOrEmpty(portEnv) ? DEFAULT_PORT : 
Integer.parseInt(portEnv);
+
+        logger.info("Apache MetaModel Membrane server initiating on port {}", 
port);
+
+        startServer(port);
+
+        logger.info("Apache MetaModel Membrane server started on port {}", 
port);
+    }
+
+    public static void startServer(int port) throws Exception {
+        final DeploymentInfo deployment = 
Servlets.deployment().setClassLoader(WebServer.class.getClassLoader());
+        deployment.setContextPath("");
+        deployment.setDeploymentName("membrane");
+        deployment.addInitParameter("contextConfigLocation", 
"classpath:context/application-context.xml");
+        deployment.setResourceManager(new FileResourceManager(new File("."), 
0));
+        deployment.addListener(Servlets.listener(ContextLoaderListener.class));
+        
deployment.addListener(Servlets.listener(RequestContextListener.class));
+        deployment.addServlet(Servlets.servlet("dispatcher", 
DispatcherServlet.class).addMapping("/*")
+                .addInitParam("contextConfigLocation", 
"classpath:context/dispatcher-servlet.xml"));
+        
deployment.addFilter(Servlets.filter(CharacterEncodingFilter.class).addInitParam("forceEncoding",
 "true")
+                .addInitParam("encoding", "UTF-8"));
+
+        final DeploymentManager manager = 
Servlets.defaultContainer().addDeployment(deployment);
+        manager.deploy();
+
+        final Undertow server = Undertow.builder().addHttpListener(port, 
"0.0.0.0").setHandler(manager.start()).build();
+        server.start();
+
+        Runtime.getRuntime().addShutdownHook(new Thread() {
+            @Override
+            public void run() {
+                // graceful shutdown of everything
+                server.stop();
+                try {
+                    manager.stop();
+                } catch (ServletException e) {
+                }
+                manager.undeploy();
+            }
+        });
+    }
+}

http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/6492107d/war/pom.xml
----------------------------------------------------------------------
diff --git a/war/pom.xml b/war/pom.xml
new file mode 100644
index 0000000..0c35c43
--- /dev/null
+++ b/war/pom.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+       xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+       <parent>
+               <groupId>org.apache.metamodel.membrane</groupId>
+               <artifactId>Membrane-parent</artifactId>
+               <version>0.1-SNAPSHOT</version>
+       </parent>
+       <modelVersion>4.0.0</modelVersion>
+       <artifactId>Membrane-war</artifactId>
+       <packaging>war</packaging>
+
+       <build>
+               <finalName>Membrane</finalName>
+       </build>
+
+       <dependencies>
+               <dependency>
+                       <groupId>org.apache.metamodel.membrane</groupId>
+                       <artifactId>Membrane-core</artifactId>
+                       <version>${project.version}</version>
+               </dependency>
+       </dependencies>
+</project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/6492107d/war/src/main/webapp/WEB-INF/web.xml
----------------------------------------------------------------------
diff --git a/war/src/main/webapp/WEB-INF/web.xml 
b/war/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..5a313ca
--- /dev/null
+++ b/war/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+       version="3.1" xmlns="http://java.sun.com/xml/ns/javaee";>
+       
+       <!-- Spring listener -->
+       <listener>
+               
<listener-class>org.springframework.web.context.ContextLoaderListener
+               </listener-class>
+       </listener>
+       <listener>
+               
<listener-class>org.springframework.web.context.request.RequestContextListener
+               </listener-class>
+       </listener>
+
+       <!-- Spring config -->
+       <context-param>
+               <param-name>contextConfigLocation</param-name>
+               
<param-value>classpath:context/application-context.xml</param-value>
+       </context-param>
+
+       <!-- Spring servlet -->
+       <servlet>
+               <servlet-name>dispatcher</servlet-name>
+               <servlet-class>org.springframework.web.servlet.DispatcherServlet
+               </servlet-class>
+               <load-on-startup>1</load-on-startup>
+               <init-param>
+                       <param-name>contextConfigLocation</param-name>
+                       
<param-value>classpath:context/dispatcher-servlet.xml</param-value>
+               </init-param>
+       </servlet>
+       <servlet-mapping>
+               <servlet-name>dispatcher</servlet-name>
+               <url-pattern>/*</url-pattern>
+       </servlet-mapping>
+
+       <filter>
+               <filter-name>CharacterEncodingFilter</filter-name>
+               
<filter-class>org.springframework.web.filter.CharacterEncodingFilter
+               </filter-class>
+               <init-param>
+                       <param-name>encoding</param-name>
+                       <param-value>UTF-8</param-value>
+               </init-param>
+               <init-param>
+                       <param-name>forceEncoding</param-name>
+                       <param-value>true</param-value>
+               </init-param>
+       </filter>
+
+       <filter-mapping>
+               <filter-name>CharacterEncodingFilter</filter-name>
+               <url-pattern>/*</url-pattern>
+       </filter-mapping>
+
+</web-app>

Reply via email to