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; + } + +}
