Added error handling component

Project: http://git-wip-us.apache.org/repos/asf/metamodel/repo
Commit: http://git-wip-us.apache.org/repos/asf/metamodel/commit/704790b1
Tree: http://git-wip-us.apache.org/repos/asf/metamodel/tree/704790b1
Diff: http://git-wip-us.apache.org/repos/asf/metamodel/diff/704790b1

Branch: refs/heads/feature/5.x/swagger-docs
Commit: 704790b16ae8754c693251149127ef12b530df7a
Parents: 76091fe
Author: Kasper Sørensen <[email protected]>
Authored: Sun Jun 12 21:33:17 2016 -0700
Committer: Kasper Sørensen <[email protected]>
Committed: Sun Jun 12 21:58:28 2016 -0700

----------------------------------------------------------------------
 pom.xml                                         |   2 +-
 .../service/app/DataSourceRegistry.java         |  16 +-
 .../service/app/InMemoryDataSourceRegistry.java |   8 +-
 .../service/app/InMemoryTenantRegistry.java     |  18 ++-
 .../metamodel/service/app/TenantRegistry.java   |  13 +-
 .../AbstractIdentifierNamingException.java      |  40 +++++
 .../DataSourceAlreadyExistException.java        |  28 ++++
 .../DataSourceNotUpdateableException.java       |  37 +++++
 .../exceptions/NoSuchDataSourceException.java   |  29 ++++
 .../app/exceptions/NoSuchTenantException.java   |  28 ++++
 .../exceptions/TenantAlreadyExistException.java |  28 ++++
 .../controllers/DataSourceController.java       |   8 +-
 .../service/controllers/RestErrorHandler.java   | 156 +++++++++++++++++++
 .../controllers/TableDataController.java        |   6 +-
 .../service/controllers/TenantController.java   |  12 +-
 .../controllers/model/RestErrorResponse.java    |  80 ++++++++++
 16 files changed, 476 insertions(+), 33 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/metamodel/blob/704790b1/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 0e9d696..678dd35 100644
--- a/pom.xml
+++ b/pom.xml
@@ -372,7 +372,7 @@ under the License.
                                                
<excludeSubProjects>false</excludeSubProjects>
                                                <excludes>
                                                        <exclude>KEYS</exclude>
-                                                       <exclude>*.md</exclude>
+                                                       
<exclude>**/*.md</exclude>
                                                        
<exclude>example-metamodel-integrationtest-configuration.properties</exclude>
                                                        
<exclude>travis-metamodel-integrationtest-configuration.properties</exclude>
                                                        
<exclude>**/src/assembly/metamodel-packaged-assembly-descriptor.xml</exclude>

http://git-wip-us.apache.org/repos/asf/metamodel/blob/704790b1/service-webapp/src/main/java/org/apache/metamodel/service/app/DataSourceRegistry.java
----------------------------------------------------------------------
diff --git 
a/service-webapp/src/main/java/org/apache/metamodel/service/app/DataSourceRegistry.java
 
b/service-webapp/src/main/java/org/apache/metamodel/service/app/DataSourceRegistry.java
index 1930354..3a9ba43 100644
--- 
a/service-webapp/src/main/java/org/apache/metamodel/service/app/DataSourceRegistry.java
+++ 
b/service-webapp/src/main/java/org/apache/metamodel/service/app/DataSourceRegistry.java
@@ -21,6 +21,10 @@ package org.apache.metamodel.service.app;
 import java.util.List;
 
 import org.apache.metamodel.DataContext;
+import org.apache.metamodel.UpdateableDataContext;
+import 
org.apache.metamodel.service.app.exceptions.DataSourceAlreadyExistException;
+import 
org.apache.metamodel.service.app.exceptions.DataSourceNotUpdateableException;
+import org.apache.metamodel.service.app.exceptions.NoSuchDataSourceException;
 
 /**
  * Represents a user's/tenant's registry of {@link DataContext}s.
@@ -29,7 +33,15 @@ public interface DataSourceRegistry {
 
     public List<String> getDataSourceNames();
 
-    public String registerDataSource(String dataContextName, 
DataSourceDefinition dataSourceDef) throws IllegalArgumentException;
+    public String registerDataSource(String dataContextName, 
DataSourceDefinition dataSourceDef) throws DataSourceAlreadyExistException;
 
-    public DataContext openDataContext(String dataSourceName) throws 
IllegalArgumentException;
+    public DataContext openDataContext(String dataSourceName) throws 
NoSuchDataSourceException;
+
+    public default UpdateableDataContext openDataContextForUpdate(String 
dataSourceName) {
+        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/blob/704790b1/service-webapp/src/main/java/org/apache/metamodel/service/app/InMemoryDataSourceRegistry.java
----------------------------------------------------------------------
diff --git 
a/service-webapp/src/main/java/org/apache/metamodel/service/app/InMemoryDataSourceRegistry.java
 
b/service-webapp/src/main/java/org/apache/metamodel/service/app/InMemoryDataSourceRegistry.java
index 3a6ecee..b99c6e7 100644
--- 
a/service-webapp/src/main/java/org/apache/metamodel/service/app/InMemoryDataSourceRegistry.java
+++ 
b/service-webapp/src/main/java/org/apache/metamodel/service/app/InMemoryDataSourceRegistry.java
@@ -25,6 +25,8 @@ import java.util.function.Supplier;
 import java.util.stream.Collectors;
 
 import org.apache.metamodel.DataContext;
+import 
org.apache.metamodel.service.app.exceptions.DataSourceAlreadyExistException;
+import org.apache.metamodel.service.app.exceptions.NoSuchDataSourceException;
 
 public class InMemoryDataSourceRegistry implements DataSourceRegistry {
 
@@ -36,9 +38,9 @@ public class InMemoryDataSourceRegistry implements 
DataSourceRegistry {
 
     @Override
     public String registerDataSource(final String name, final 
DataSourceDefinition dataSourceDef)
-            throws IllegalArgumentException {
+            throws DataSourceAlreadyExistException {
         if (dataSources.containsKey(name)) {
-            throw new IllegalArgumentException("DataContext already exist: " + 
name);
+            throw new DataSourceAlreadyExistException(name);
         }
 
         dataSources.put(name, new DataContextSupplier(name, dataSourceDef));
@@ -54,7 +56,7 @@ public class InMemoryDataSourceRegistry implements 
DataSourceRegistry {
     public DataContext openDataContext(String name) {
         final Supplier<DataContext> supplier = dataSources.get(name);
         if (supplier == null) {
-            throw new IllegalArgumentException("No such DataContext: " + name);
+            throw new NoSuchDataSourceException(name);
         }
         return supplier.get();
     }

http://git-wip-us.apache.org/repos/asf/metamodel/blob/704790b1/service-webapp/src/main/java/org/apache/metamodel/service/app/InMemoryTenantRegistry.java
----------------------------------------------------------------------
diff --git 
a/service-webapp/src/main/java/org/apache/metamodel/service/app/InMemoryTenantRegistry.java
 
b/service-webapp/src/main/java/org/apache/metamodel/service/app/InMemoryTenantRegistry.java
index c74fb22..6ac5bec 100644
--- 
a/service-webapp/src/main/java/org/apache/metamodel/service/app/InMemoryTenantRegistry.java
+++ 
b/service-webapp/src/main/java/org/apache/metamodel/service/app/InMemoryTenantRegistry.java
@@ -23,6 +23,9 @@ import java.util.List;
 import java.util.Map;
 import java.util.stream.Collectors;
 
+import org.apache.metamodel.service.app.exceptions.NoSuchTenantException;
+import org.apache.metamodel.service.app.exceptions.TenantAlreadyExistException;
+
 /**
  * In-memory {@link TenantRegistry}. This is not particularly
  * production-friendly as it is non-persistent, but it is useful for demo
@@ -43,13 +46,17 @@ public class InMemoryTenantRegistry implements 
TenantRegistry {
 
     @Override
     public TenantContext getTenantContext(String tenantIdentifier) {
-        return tenants.get(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 IllegalArgumentException("Tenant already exist: " + 
tenantIdentifier);
+            throw new TenantAlreadyExistException(tenantIdentifier);
         }
         final InMemoryTenantContext tenantContext = new 
InMemoryTenantContext(tenantIdentifier);
         tenants.put(tenantIdentifier, tenantContext);
@@ -57,8 +64,11 @@ public class InMemoryTenantRegistry implements 
TenantRegistry {
     }
 
     @Override
-    public boolean deleteTenantContext(String tenantIdentifier) {
-        return tenants.remove(tenantIdentifier) != null;
+    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/blob/704790b1/service-webapp/src/main/java/org/apache/metamodel/service/app/TenantRegistry.java
----------------------------------------------------------------------
diff --git 
a/service-webapp/src/main/java/org/apache/metamodel/service/app/TenantRegistry.java
 
b/service-webapp/src/main/java/org/apache/metamodel/service/app/TenantRegistry.java
index 450db2d..5c02821 100644
--- 
a/service-webapp/src/main/java/org/apache/metamodel/service/app/TenantRegistry.java
+++ 
b/service-webapp/src/main/java/org/apache/metamodel/service/app/TenantRegistry.java
@@ -20,6 +20,9 @@ package org.apache.metamodel.service.app;
 
 import java.util.List;
 
+import org.apache.metamodel.service.app.exceptions.NoSuchTenantException;
+import org.apache.metamodel.service.app.exceptions.TenantAlreadyExistException;
+
 /**
  * Represents the application's central registry of tenants
  */
@@ -27,9 +30,9 @@ public interface TenantRegistry {
 
     public List<String> getTenantIdentifiers();
 
-    public TenantContext getTenantContext(String tenantIdentifier);
-    
-    public TenantContext createTenantContext(String tenantIdentifier) throws 
IllegalArgumentException;
-    
-    public boolean deleteTenantContext(String tenantIdentifier);
+    public TenantContext getTenantContext(String tenantIdentifier) throws 
NoSuchTenantException;
+
+    public TenantContext createTenantContext(String tenantIdentifier) throws 
TenantAlreadyExistException;
+
+    public void deleteTenantContext(String tenantIdentifier) throws 
NoSuchTenantException;
 }

http://git-wip-us.apache.org/repos/asf/metamodel/blob/704790b1/service-webapp/src/main/java/org/apache/metamodel/service/app/exceptions/AbstractIdentifierNamingException.java
----------------------------------------------------------------------
diff --git 
a/service-webapp/src/main/java/org/apache/metamodel/service/app/exceptions/AbstractIdentifierNamingException.java
 
b/service-webapp/src/main/java/org/apache/metamodel/service/app/exceptions/AbstractIdentifierNamingException.java
new file mode 100644
index 0000000..98f5892
--- /dev/null
+++ 
b/service-webapp/src/main/java/org/apache/metamodel/service/app/exceptions/AbstractIdentifierNamingException.java
@@ -0,0 +1,40 @@
+/**
+ * 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.service.app.exceptions;
+
+import org.apache.metamodel.MetaModelException;
+
+/**
+ * Exception super class for any exception that arises because an identifier
+ * (name, ID or such) is invalid for a specific context.
+ */
+public class AbstractIdentifierNamingException extends MetaModelException {
+
+    private static final long serialVersionUID = 1L;
+    private final String identifier;
+
+    public AbstractIdentifierNamingException(String identifier) {
+        super("Illegal value: " + identifier);
+        this.identifier = identifier;
+    }
+
+    public String getIdentifier() {
+        return identifier;
+    }
+}

http://git-wip-us.apache.org/repos/asf/metamodel/blob/704790b1/service-webapp/src/main/java/org/apache/metamodel/service/app/exceptions/DataSourceAlreadyExistException.java
----------------------------------------------------------------------
diff --git 
a/service-webapp/src/main/java/org/apache/metamodel/service/app/exceptions/DataSourceAlreadyExistException.java
 
b/service-webapp/src/main/java/org/apache/metamodel/service/app/exceptions/DataSourceAlreadyExistException.java
new file mode 100644
index 0000000..3d5d650
--- /dev/null
+++ 
b/service-webapp/src/main/java/org/apache/metamodel/service/app/exceptions/DataSourceAlreadyExistException.java
@@ -0,0 +1,28 @@
+/**
+ * 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.service.app.exceptions;
+
+public class DataSourceAlreadyExistException extends 
AbstractIdentifierNamingException {
+
+    private static final long serialVersionUID = 1L;
+
+    public DataSourceAlreadyExistException(String name) {
+        super(name);
+    }
+}

http://git-wip-us.apache.org/repos/asf/metamodel/blob/704790b1/service-webapp/src/main/java/org/apache/metamodel/service/app/exceptions/DataSourceNotUpdateableException.java
----------------------------------------------------------------------
diff --git 
a/service-webapp/src/main/java/org/apache/metamodel/service/app/exceptions/DataSourceNotUpdateableException.java
 
b/service-webapp/src/main/java/org/apache/metamodel/service/app/exceptions/DataSourceNotUpdateableException.java
new file mode 100644
index 0000000..eb828cc
--- /dev/null
+++ 
b/service-webapp/src/main/java/org/apache/metamodel/service/app/exceptions/DataSourceNotUpdateableException.java
@@ -0,0 +1,37 @@
+/**
+ * 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.service.app.exceptions;
+
+import org.apache.metamodel.MetaModelException;
+
+public class DataSourceNotUpdateableException extends MetaModelException {
+
+    private static final long serialVersionUID = 1L;
+
+    private final String dataSourceName;
+
+    public DataSourceNotUpdateableException(String dataSourceName) {
+        super(dataSourceName);
+        this.dataSourceName = dataSourceName;
+    }
+
+    public String getDataSourceName() {
+        return dataSourceName;
+    }
+}

http://git-wip-us.apache.org/repos/asf/metamodel/blob/704790b1/service-webapp/src/main/java/org/apache/metamodel/service/app/exceptions/NoSuchDataSourceException.java
----------------------------------------------------------------------
diff --git 
a/service-webapp/src/main/java/org/apache/metamodel/service/app/exceptions/NoSuchDataSourceException.java
 
b/service-webapp/src/main/java/org/apache/metamodel/service/app/exceptions/NoSuchDataSourceException.java
new file mode 100644
index 0000000..b59f016
--- /dev/null
+++ 
b/service-webapp/src/main/java/org/apache/metamodel/service/app/exceptions/NoSuchDataSourceException.java
@@ -0,0 +1,29 @@
+/**
+ * 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.service.app.exceptions;
+
+public class NoSuchDataSourceException extends 
AbstractIdentifierNamingException {
+
+    private static final long serialVersionUID = 1L;
+
+    public NoSuchDataSourceException(String name) {
+        super(name);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/metamodel/blob/704790b1/service-webapp/src/main/java/org/apache/metamodel/service/app/exceptions/NoSuchTenantException.java
----------------------------------------------------------------------
diff --git 
a/service-webapp/src/main/java/org/apache/metamodel/service/app/exceptions/NoSuchTenantException.java
 
b/service-webapp/src/main/java/org/apache/metamodel/service/app/exceptions/NoSuchTenantException.java
new file mode 100644
index 0000000..b123ae2
--- /dev/null
+++ 
b/service-webapp/src/main/java/org/apache/metamodel/service/app/exceptions/NoSuchTenantException.java
@@ -0,0 +1,28 @@
+/**
+ * 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.service.app.exceptions;
+
+public class NoSuchTenantException extends AbstractIdentifierNamingException {
+
+    private static final long serialVersionUID = 1L;
+
+    public NoSuchTenantException(String tenantIdentifier) {
+        super(tenantIdentifier);
+    }
+}

http://git-wip-us.apache.org/repos/asf/metamodel/blob/704790b1/service-webapp/src/main/java/org/apache/metamodel/service/app/exceptions/TenantAlreadyExistException.java
----------------------------------------------------------------------
diff --git 
a/service-webapp/src/main/java/org/apache/metamodel/service/app/exceptions/TenantAlreadyExistException.java
 
b/service-webapp/src/main/java/org/apache/metamodel/service/app/exceptions/TenantAlreadyExistException.java
new file mode 100644
index 0000000..f24c114
--- /dev/null
+++ 
b/service-webapp/src/main/java/org/apache/metamodel/service/app/exceptions/TenantAlreadyExistException.java
@@ -0,0 +1,28 @@
+/**
+ * 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.service.app.exceptions;
+
+public class TenantAlreadyExistException extends 
AbstractIdentifierNamingException {
+
+    private static final long serialVersionUID = 1L;
+
+    public TenantAlreadyExistException(String tenantIdentifier) {
+        super(tenantIdentifier);
+    }
+}

http://git-wip-us.apache.org/repos/asf/metamodel/blob/704790b1/service-webapp/src/main/java/org/apache/metamodel/service/controllers/DataSourceController.java
----------------------------------------------------------------------
diff --git 
a/service-webapp/src/main/java/org/apache/metamodel/service/controllers/DataSourceController.java
 
b/service-webapp/src/main/java/org/apache/metamodel/service/controllers/DataSourceController.java
index 8be66b1..9c678ff 100644
--- 
a/service-webapp/src/main/java/org/apache/metamodel/service/controllers/DataSourceController.java
+++ 
b/service-webapp/src/main/java/org/apache/metamodel/service/controllers/DataSourceController.java
@@ -28,10 +28,11 @@ import javax.validation.Valid;
 import javax.ws.rs.core.UriBuilder;
 
 import org.apache.metamodel.DataContext;
+import org.apache.metamodel.UpdateableDataContext;
 import org.apache.metamodel.service.app.TenantContext;
 import org.apache.metamodel.service.app.TenantRegistry;
-import org.apache.metamodel.service.controllers.model.RestLink;
 import org.apache.metamodel.service.controllers.model.RestDataSourceDefinition;
+import org.apache.metamodel.service.controllers.model.RestLink;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.MediaType;
 import org.springframework.web.bind.annotation.PathVariable;
@@ -73,13 +74,14 @@ public class DataSourceController {
         final String tenantName = tenantContext.getTenantName();
         final UriBuilder uriBuilder = 
UriBuilder.fromPath("/{tenant}/{dataContext}/s/{schema}");
 
-        final List<RestLink> schemaLinks = 
Arrays.stream(dataContext.getSchemaNames()).map(s -> new RestLink(s, uriBuilder
-                .build(tenantName, dataSourceName, 
s))).collect(Collectors.toList());
+        final List<RestLink> schemaLinks = 
Arrays.stream(dataContext.getSchemaNames()).map(s -> new RestLink(s,
+                uriBuilder.build(tenantName, dataSourceName, 
s))).collect(Collectors.toList());
 
         final Map<String, Object> map = new LinkedHashMap<>();
         map.put("type", "datasource");
         map.put("name", dataSourceName);
         map.put("tenant", tenantName);
+        map.put("updateable", dataContext instanceof UpdateableDataContext);
         map.put("query", 
UriBuilder.fromPath("/{tenant}/{dataContext}/query").build(tenantName, 
dataSourceName));
         map.put("schemas", schemaLinks);
         return map;

http://git-wip-us.apache.org/repos/asf/metamodel/blob/704790b1/service-webapp/src/main/java/org/apache/metamodel/service/controllers/RestErrorHandler.java
----------------------------------------------------------------------
diff --git 
a/service-webapp/src/main/java/org/apache/metamodel/service/controllers/RestErrorHandler.java
 
b/service-webapp/src/main/java/org/apache/metamodel/service/controllers/RestErrorHandler.java
new file mode 100644
index 0000000..690ee0e
--- /dev/null
+++ 
b/service-webapp/src/main/java/org/apache/metamodel/service/controllers/RestErrorHandler.java
@@ -0,0 +1,156 @@
+/**
+ * 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.service.controllers;
+
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.metamodel.query.parser.QueryParserException;
+import 
org.apache.metamodel.service.app.exceptions.AbstractIdentifierNamingException;
+import 
org.apache.metamodel.service.app.exceptions.DataSourceAlreadyExistException;
+import 
org.apache.metamodel.service.app.exceptions.DataSourceNotUpdateableException;
+import org.apache.metamodel.service.app.exceptions.NoSuchDataSourceException;
+import org.apache.metamodel.service.app.exceptions.NoSuchTenantException;
+import org.apache.metamodel.service.app.exceptions.TenantAlreadyExistException;
+import org.apache.metamodel.service.controllers.model.RestErrorResponse;
+import org.springframework.http.HttpStatus;
+import org.springframework.validation.BindingResult;
+import org.springframework.validation.FieldError;
+import org.springframework.validation.ObjectError;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+@ControllerAdvice
+public class RestErrorHandler {
+
+    /**
+     * Method binding issues (raised by Spring framework) - mapped to
+     * BAD_REQUEST.
+     * 
+     * @param ex
+     * @return
+     */
+    @ExceptionHandler(MethodArgumentNotValidException.class)
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    @ResponseBody
+    public RestErrorResponse 
processValidationError(MethodArgumentNotValidException ex) {
+        final BindingResult result = ex.getBindingResult();
+
+        final Map<String, Object> globalErrorsMap = new LinkedHashMap<>();
+        final List<ObjectError> globalErrors = result.getGlobalErrors();
+        for (ObjectError objectError : globalErrors) {
+            globalErrorsMap.put(objectError.getObjectName(), 
objectError.getDefaultMessage());
+        }
+
+        final List<FieldError> fieldErrors = result.getFieldErrors();
+        final Map<String, Object> fieldErrorsMap = new LinkedHashMap<>();
+        for (FieldError fieldError : fieldErrors) {
+            fieldErrorsMap.put(fieldError.getObjectName() + '.' + 
fieldError.getField(), fieldError
+                    .getDefaultMessage());
+        }
+
+        final Map<String, Object> additionalDetails = new LinkedHashMap<>();
+        if (!globalErrorsMap.isEmpty()) {
+            additionalDetails.put("global-errors", globalErrorsMap);
+        }
+        if (!fieldErrorsMap.isEmpty()) {
+            additionalDetails.put("field-errors", fieldErrorsMap);
+        }
+        final RestErrorResponse errorResponse = new 
RestErrorResponse(HttpStatus.BAD_REQUEST.value(),
+                "Failed to validate request");
+        if (!additionalDetails.isEmpty()) {
+            errorResponse.setAdditionalDetails(additionalDetails);
+        }
+        return errorResponse;
+    }
+
+    /**
+     * No such [Entity] exception handler method - mapped to NOT_FOUND.
+     * 
+     * @param ex
+     * @return
+     */
+    @ExceptionHandler({ NoSuchTenantException.class, 
NoSuchDataSourceException.class })
+    @ResponseStatus(HttpStatus.NOT_FOUND)
+    @ResponseBody
+    public RestErrorResponse 
processNoSuchEntity(AbstractIdentifierNamingException ex) {
+        return new RestErrorResponse(HttpStatus.NOT_FOUND.value(), "Not found: 
" + ex.getIdentifier());
+    }
+
+    /**
+     * [Entity] already exist exception handler method - mapped to CONFLICT.
+     * 
+     * @param ex
+     * @return
+     */
+    @ExceptionHandler({ TenantAlreadyExistException.class, 
DataSourceAlreadyExistException.class })
+    @ResponseStatus(HttpStatus.CONFLICT)
+    @ResponseBody
+    public RestErrorResponse 
processEntityAlreadyExist(AbstractIdentifierNamingException ex) {
+        return new RestErrorResponse(HttpStatus.CONFLICT.value(), "Already 
exist: " + ex.getIdentifier());
+    }
+
+    /**
+     * DataSource not updateable exception handler method - mapped to
+     * BAD_REQUEST.
+     * 
+     * @param ex
+     * @return
+     */
+    @ExceptionHandler(DataSourceNotUpdateableException.class)
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    @ResponseBody
+    public RestErrorResponse 
processDataSourceNotUpdateable(DataSourceNotUpdateableException ex) {
+        return new RestErrorResponse(HttpStatus.BAD_REQUEST.value(), 
"DataSource not updateable: " + ex
+                .getDataSourceName());
+    }
+
+    /**
+     * Query parsing exception - mapped to BAD_REQUEST.
+     * 
+     * @param ex
+     * @return
+     */
+    @ExceptionHandler(QueryParserException.class)
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    @ResponseBody
+    public RestErrorResponse processQueryParsingError(QueryParserException ex) 
{
+        return new RestErrorResponse(HttpStatus.BAD_REQUEST.value(), 
ex.getMessage());
+    }
+
+    /**
+     * Catch-all exception handler method - mapped to INTERNAL_SERVER_ERROR.
+     * 
+     * @param ex
+     * @return
+     */
+    @ExceptionHandler(Exception.class)
+    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
+    @ResponseBody
+    public RestErrorResponse processAnyException(Exception ex) {
+        final Map<String, Object> additionalDetails = new HashMap<>();
+        additionalDetails.put("exception_type", ex.getClass().getName());
+        return new RestErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR.value(), 
ex.getMessage(), additionalDetails);
+    }
+}

http://git-wip-us.apache.org/repos/asf/metamodel/blob/704790b1/service-webapp/src/main/java/org/apache/metamodel/service/controllers/TableDataController.java
----------------------------------------------------------------------
diff --git 
a/service-webapp/src/main/java/org/apache/metamodel/service/controllers/TableDataController.java
 
b/service-webapp/src/main/java/org/apache/metamodel/service/controllers/TableDataController.java
index a12bbac..e25258f 100644
--- 
a/service-webapp/src/main/java/org/apache/metamodel/service/controllers/TableDataController.java
+++ 
b/service-webapp/src/main/java/org/apache/metamodel/service/controllers/TableDataController.java
@@ -77,11 +77,7 @@ public class TableDataController {
             @PathVariable("table") String tableId, @RequestBody Map<String, 
Object> inputMap) {
 
         final TenantContext tenantContext = 
tenantRegistry.getTenantContext(tenantId);
-        final DataContext dataContext = 
tenantContext.getDataSourceRegistry().openDataContext(dataSourceName);
-        if (!(dataContext instanceof UpdateableDataContext)) {
-            throw new UnsupportedOperationException("Cannot perform updates on 
read-only datasource: "
-                    + dataSourceName);
-        }
+        final UpdateableDataContext dataContext = 
tenantContext.getDataSourceRegistry().openDataContextForUpdate(dataSourceName);
 
         final Schema schema = dataContext.getSchemaByName(schemaId);
         final Table table = schema.getTableByName(tableId);

http://git-wip-us.apache.org/repos/asf/metamodel/blob/704790b1/service-webapp/src/main/java/org/apache/metamodel/service/controllers/TenantController.java
----------------------------------------------------------------------
diff --git 
a/service-webapp/src/main/java/org/apache/metamodel/service/controllers/TenantController.java
 
b/service-webapp/src/main/java/org/apache/metamodel/service/controllers/TenantController.java
index ef3011f..9582bbe 100644
--- 
a/service-webapp/src/main/java/org/apache/metamodel/service/controllers/TenantController.java
+++ 
b/service-webapp/src/main/java/org/apache/metamodel/service/controllers/TenantController.java
@@ -51,10 +51,6 @@ public class TenantController {
     @ResponseBody
     public Map<String, Object> getTenant(@PathVariable("tenant") String 
tenantName) {
         final TenantContext tenantContext = 
tenantRegistry.getTenantContext(tenantName);
-        if (tenantContext == null) {
-            throw new IllegalArgumentException("No such tenant: " + 
tenantName);
-        }
-
         final String tenantNameNormalized = tenantContext.getTenantName();
 
         final UriBuilder uriBuilder = 
UriBuilder.fromPath("/{tenant}/{datasource}");
@@ -86,16 +82,12 @@ public class TenantController {
     @RequestMapping(method = RequestMethod.DELETE)
     @ResponseBody
     public Map<String, Object> deleteTenant(@PathVariable("tenant") String 
tenantName) {
-        final boolean deleted = tenantRegistry.deleteTenantContext(tenantName);
-
-        if (!deleted) {
-            throw new IllegalArgumentException("No such tenant: " + 
tenantName);
-        }
+        tenantRegistry.deleteTenantContext(tenantName);
 
         final Map<String, Object> map = new LinkedHashMap<>();
         map.put("type", "tenant");
         map.put("name", tenantName);
-        map.put("deleted", deleted);
+        map.put("deleted", true);
 
         return map;
     }

http://git-wip-us.apache.org/repos/asf/metamodel/blob/704790b1/service-webapp/src/main/java/org/apache/metamodel/service/controllers/model/RestErrorResponse.java
----------------------------------------------------------------------
diff --git 
a/service-webapp/src/main/java/org/apache/metamodel/service/controllers/model/RestErrorResponse.java
 
b/service-webapp/src/main/java/org/apache/metamodel/service/controllers/model/RestErrorResponse.java
new file mode 100644
index 0000000..ed27dfe
--- /dev/null
+++ 
b/service-webapp/src/main/java/org/apache/metamodel/service/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.service.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;
+    }
+
+}

Reply via email to