/** * Copyright 2016 Hortonworks. * <p> * Licensed 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 * <p> * http://www.apache.org/licenses/LICENSE-2.0 * <p> * 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 com.hortonworks.registries.schemaregistry.webservice; import com.codahale.metrics.annotation.Timed; import com.google.common.base.Preconditions; import com.hortonworks.registries.common.catalog.CatalogResponse; import com.hortonworks.registries.common.ha.LeadershipParticipant; import com.hortonworks.registries.common.util.WSUtils; import com.hortonworks.registries.schemaregistry.CompatibilityResult; import com.hortonworks.registries.schemaregistry.ISchemaRegistry; import com.hortonworks.registries.schemaregistry.SchemaFieldInfo; import com.hortonworks.registries.schemaregistry.SchemaFieldQuery; import com.hortonworks.registries.schemaregistry.SchemaMetadata; import com.hortonworks.registries.schemaregistry.SchemaMetadataInfo; import com.hortonworks.registries.schemaregistry.SchemaProviderInfo; import com.hortonworks.registries.schemaregistry.SchemaVersion; import com.hortonworks.registries.schemaregistry.SchemaVersionInfo; import com.hortonworks.registries.schemaregistry.SchemaVersionKey; import com.hortonworks.registries.schemaregistry.SerDesInfo; import com.hortonworks.registries.schemaregistry.SerDesPair; import com.hortonworks.registries.schemaregistry.errors.IncompatibleSchemaException; import com.hortonworks.registries.schemaregistry.errors.InvalidSchemaException; import com.hortonworks.registries.schemaregistry.errors.SchemaNotFoundException; import com.hortonworks.registries.schemaregistry.errors.UnsupportedSchemaTypeException; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; import org.apache.commons.io.IOUtils; import org.glassfish.jersey.media.multipart.FormDataContentDisposition; import org.glassfish.jersey.media.multipart.FormDataParam; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.Consumes; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.Context; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; import javax.ws.rs.core.StreamingOutput; import javax.ws.rs.core.UriInfo; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Supplier; /** * Schema Registry resource that provides schema registry REST service. */ @Path("/v1/schemaregistry") @Api(value = "/api/v1/schemaregistry", description = "Endpoint for Schema Registry service") @Produces(MediaType.APPLICATION_JSON) public class SchemaRegistryResource { private static final Logger LOG = LoggerFactory.getLogger(SchemaRegistryResource.class); public static final String THROW_ERROR_IF_EXISTS = "_throwErrorIfExists"; public static final String THROW_ERROR_IF_EXISTS_LOWER_CASE = THROW_ERROR_IF_EXISTS.toLowerCase(); private final ISchemaRegistry schemaRegistry; private final AtomicReference<LeadershipParticipant> leadershipParticipant; public SchemaRegistryResource(ISchemaRegistry schemaRegistry, AtomicReference<LeadershipParticipant> leadershipParticipant) { Preconditions.checkNotNull(schemaRegistry, "SchemaRegistry can not be null"); Preconditions.checkNotNull(leadershipParticipant, "LeadershipParticipant can not be null"); this.schemaRegistry = schemaRegistry; this.leadershipParticipant = leadershipParticipant; } // Hack: Adding number in front of sections to get the ordering in generated swagger documentation correct private static final String OPERATION_GROUP_SCHEMA = "1. Schema"; private static final String OPERATION_GROUP_SERDE = "2. Serializer/Deserializer"; private static final String OPERATION_GROUP_OTHER = "3. Other"; @GET @Path("/schemaproviders") @ApiOperation(value = "Get list of registered Schema Providers", notes = "The Schema Registry supports different types of schemas, such as Avro, JSON etc. " + "" + "A Schema Provider is needed for each type of schema supported by the Schema Registry. " + "Schema Provider supports defining schema, serializing and deserializing data using the schema, " + " and checking compatibility between different versions of the schema.", response = SchemaProviderInfo.class, responseContainer = "List", tags = OPERATION_GROUP_OTHER) @Timed public Response getRegisteredSchemaProviderInfos(@Context UriInfo uriInfo) { try { Collection<SchemaProviderInfo> schemaProviderInfos = schemaRegistry.getRegisteredSchemaProviderInfos(); return WSUtils.respondEntities(schemaProviderInfos, Response.Status.OK); } catch (Exception ex) { LOG.error("Encountered error while listing schemas", ex); return WSUtils.respond(Response.Status.INTERNAL_SERVER_ERROR, CatalogResponse.ResponseMessage.EXCEPTION, ex.getMessage()); } } /** * Checks whether the current instance is a leader. If so, it invokes the given {@code supplier}, else current * request is redirected to the leader node in registry cluster. * * @param uriInfo * @param supplier * @return */ private Response handleLeaderAction(UriInfo uriInfo, Supplier<Response> supplier) { LOG.info("URI info [{}]", uriInfo.getRequestUri()); if (!leadershipParticipant.get().isLeader()) { URI location = null; try { String currentLeaderLoc = leadershipParticipant.get().getCurrentLeader(); URI leaderServerUrl = new URI(currentLeaderLoc); URI requestUri = uriInfo.getRequestUri(); location = new URI(leaderServerUrl.getScheme(), leaderServerUrl.getAuthority(), requestUri.getPath(), requestUri.getQuery(), requestUri.getFragment()); LOG.info("Redirecting to URI [{}] as this instance is not the leader", location); } catch (Exception e) { throw new RuntimeException(e); } return Response.temporaryRedirect(location).build(); } else { LOG.info("Invoking here as this instance is the leader"); return supplier.get(); } } @GET @Path("/schemas") @ApiOperation(value = "Get list of schemas by filtering with the given query parameters", response = SchemaMetadata.class, responseContainer = "List", tags = OPERATION_GROUP_SCHEMA) @Timed public Response listSchemas(@Context UriInfo uriInfo) { try { MultivaluedMap<String, String> queryParameters = uriInfo.getQueryParameters(); Map<String, String> filters = new HashMap<>(); for (Map.Entry<String, List<String>> entry : queryParameters.entrySet()) { List<String> value = entry.getValue(); filters.put(entry.getKey(), value != null && !value.isEmpty() ? value.get(0) : null); } Collection<SchemaMetadata> schemaMetadatas = schemaRegistry.findSchemaMetadata(filters); return WSUtils.respondEntities(schemaMetadatas, Response.Status.OK); } catch (Exception ex) { LOG.error("Encountered error while listing schemas", ex); return WSUtils.respond(Response.Status.INTERNAL_SERVER_ERROR, CatalogResponse.ResponseMessage.EXCEPTION, ex.getMessage()); } } @GET @Path("/schemas/search/fields") @ApiOperation(value = "Search for schemas containing the given field names", notes = "Search the schemas for given field names and return a list of schemas that contain the field.", response = SchemaVersionKey.class, responseContainer = "List", tags = OPERATION_GROUP_SCHEMA) @Timed public Response findSchemasByFields(@Context UriInfo uriInfo) { MultivaluedMap<String, String> queryParameters = uriInfo.getQueryParameters(); try { Collection<SchemaVersionKey> schemaVersionKeys = schemaRegistry.findSchemasWithFields(buildSchemaFieldQuery(queryParameters)); return WSUtils.respondEntities(schemaVersionKeys, Response.Status.OK); } catch (Exception ex) { LOG.error("Encountered error while finding schemas for given fields [{}]", queryParameters, ex); return WSUtils.respond(Response.Status.INTERNAL_SERVER_ERROR, CatalogResponse.ResponseMessage.EXCEPTION, ex.getMessage()); } } private SchemaFieldQuery buildSchemaFieldQuery(MultivaluedMap<String, String> queryParameters) { SchemaFieldQuery.Builder builder = new SchemaFieldQuery.Builder(); for (Map.Entry<String, List<String>> entry : queryParameters.entrySet()) { List<String> entryValue = entry.getValue(); String value = entryValue != null && !entryValue.isEmpty() ? entryValue.get(0) : null; if (value != null) { if (SchemaFieldInfo.FIELD_NAMESPACE.equals(entry.getKey())) { builder.namespace(value); } else if (SchemaFieldInfo.NAME.equals(entry.getKey())) { builder.name(value); } else if (SchemaFieldInfo.TYPE.equals(entry.getKey())) { builder.type(value); } } } return builder.build(); } @POST @Path("/schemas") @ApiOperation(value = "Create a schema if it does not already exist", notes = "Creates a schema with the given schema information if it does not already exist." + " A unique schema identifier is returned.", response = Long.class, tags = OPERATION_GROUP_SCHEMA) @Timed public Response addSchemaInfo(@ApiParam(value = "Schema to be added to the registry", required = true) SchemaMetadata schemaMetadataInfo, @Context UriInfo uriInfo, @Context HttpHeaders httpHeaders) { return handleLeaderAction(uriInfo, () -> { Response response; try { schemaMetadataInfo.trim(); checkValueAsNullOrEmpty("Schema name", schemaMetadataInfo.getName()); checkValueAsNullOrEmpty("Schema type", schemaMetadataInfo.getType()); boolean throwErrorIfExists = isThrowErrorIfExists(httpHeaders); Long schemaId = schemaRegistry.addSchemaMetadata(schemaMetadataInfo, throwErrorIfExists); response = WSUtils.respondEntity(schemaId, Response.Status.CREATED); } catch (IllegalArgumentException ex) { LOG.error("Expected parameter is invalid", schemaMetadataInfo, ex); response = WSUtils.respond(Response.Status.BAD_REQUEST, CatalogResponse.ResponseMessage.BAD_REQUEST_PARAM_MISSING, ex.getMessage()); } catch (UnsupportedSchemaTypeException ex) { LOG.error("Unsupported schema type encountered while adding schema metadata [{}]", schemaMetadataInfo, ex); response = WSUtils.respond(Response.Status.BAD_REQUEST, CatalogResponse.ResponseMessage.UNSUPPORTED_SCHEMA_TYPE, ex.getMessage()); } catch (Exception ex) { LOG.error("Error encountered while adding schema info [{}] ", schemaMetadataInfo, ex); response = WSUtils.respond(Response.Status.INTERNAL_SERVER_ERROR, CatalogResponse.ResponseMessage.EXCEPTION, String.format("Storing the given SchemaMetadata [%s] is failed",schemaMetadataInfo.toString())); } return response; }); } private boolean isThrowErrorIfExists(HttpHeaders httpHeaders) { List<String> values = httpHeaders.getRequestHeader(THROW_ERROR_IF_EXISTS); if(values != null) { values = httpHeaders.getRequestHeader(THROW_ERROR_IF_EXISTS_LOWER_CASE); } return values != null && !values.isEmpty() && Boolean.parseBoolean(values.get(0)); } @GET @Path("/schemas/{name}") @ApiOperation(value = "Get schema information for the given schema name", response = SchemaMetadataInfo.class, tags = OPERATION_GROUP_SCHEMA) @Timed public Response getSchemaInfo(@ApiParam(value = "Schema name", required = true) @PathParam("name") String schemaName) { Response response; try { SchemaMetadataInfo schemaMetadataInfo = schemaRegistry.getSchemaMetadata(schemaName); if (schemaMetadataInfo != null) { response = WSUtils.respondEntity(schemaMetadataInfo, Response.Status.OK); } else { response = WSUtils.respond(Response.Status.NOT_FOUND, CatalogResponse.ResponseMessage.ENTITY_NOT_FOUND, schemaName); } } catch (Exception ex) { LOG.error("Encountered error while retrieving SchemaInfo with name: [{}]", schemaName, ex); response = WSUtils.respond(Response.Status.INTERNAL_SERVER_ERROR, CatalogResponse.ResponseMessage.EXCEPTION, ex.getMessage()); } return response; } @GET @Path("/schemasById/{schemaId}") @ApiOperation(value = "Get schema for a given schema identifier", response = SchemaMetadataInfo.class, tags = OPERATION_GROUP_SCHEMA) @Timed public Response getSchemaInfo(@ApiParam(value = "Schema identifier", required = true) @PathParam("schemaId") Long schemaId) { Response response; try { SchemaMetadataInfo schemaMetadataInfo = schemaRegistry.getSchemaMetadata(schemaId); if (schemaMetadataInfo != null) { response = WSUtils.respondEntity(schemaMetadataInfo, Response.Status.OK); } else { response = WSUtils.respond(Response.Status.NOT_FOUND, CatalogResponse.ResponseMessage.ENTITY_NOT_FOUND, schemaId.toString()); } } catch (Exception ex) { LOG.error("Encountered error while retrieving SchemaInfo with schemaId: [{}]", schemaId, ex); response = WSUtils.respond(Response.Status.INTERNAL_SERVER_ERROR, CatalogResponse.ResponseMessage.EXCEPTION, ex.getMessage()); } return response; } @POST @Path("/schemas/{name}/versions/upload") @Consumes(MediaType.MULTIPART_FORM_DATA) @ApiOperation(value = "Register a new version of the schema by uploading schema version text", notes = "Registers the given schema version to schema with name if the given file content is not registered as a version for this schema, " + "and returns respective version number." + "In case of incompatible schema errors, it throws error message like 'Unable to read schema: <> using schema <>' ", response = Integer.class, tags = OPERATION_GROUP_SCHEMA) @Timed public Response uploadSchemaVersion(@ApiParam(value = "Schema name", required = true) @PathParam("name") String schemaName, @ApiParam(value = "Schema version text file to be uploaded", required = true) @FormDataParam("file") final InputStream inputStream, @ApiParam(value = "Description about the schema version to be uploaded", required = true) @FormDataParam("description") final String description, @Context UriInfo uriInfo) { return handleLeaderAction(uriInfo, () -> { Response response; SchemaVersion schemaVersion = null; try { schemaVersion = new SchemaVersion(IOUtils.toString(inputStream, "UTF-8"), description); response = addSchema(schemaName, schemaVersion, uriInfo); } catch (IOException ex) { LOG.error("Encountered error while adding schema [{}] with key [{}]", schemaVersion, schemaName, ex, ex); response = WSUtils.respond(Response.Status.INTERNAL_SERVER_ERROR, CatalogResponse.ResponseMessage.EXCEPTION, ex.getMessage()); } return response; }); } @POST @Path("/schemas/{name}/versions") @ApiOperation(value = "Register a new version of the schema", notes = "Registers the given schema version to schema with name if the given schemaText is not registered as a version for this schema, " + "and returns respective version number." + "In case of incompatible schema errors, it throws error message like 'Unable to read schema: <> using schema <>' ", response = Integer.class, tags = OPERATION_GROUP_SCHEMA) @Timed public Response addSchema(@ApiParam(value = "Schema name", required = true) @PathParam("name") String schemaName, @ApiParam(value = "Details about the schema", required = true) SchemaVersion schemaVersion, @Context UriInfo uriInfo) { return handleLeaderAction(uriInfo, () -> { Response response; try { LOG.info("schemaVersion for [{}] is [{}]", schemaName, schemaVersion); Integer version = schemaRegistry.addSchemaVersion(schemaName, schemaVersion.getSchemaText(), schemaVersion.getDescription()); response = WSUtils.respondEntity(version, Response.Status.CREATED); } catch (InvalidSchemaException ex) { LOG.error("Invalid schema error encountered while adding schema [{}] with key [{}]", schemaVersion, schemaName, ex); response = WSUtils.respond(Response.Status.BAD_REQUEST, CatalogResponse.ResponseMessage.INVALID_SCHEMA, ex.getMessage()); } catch (IncompatibleSchemaException ex) { LOG.error("Incompatible schema error encountered while adding schema [{}] with key [{}]", schemaVersion, schemaName, ex); response = WSUtils.respond(Response.Status.BAD_REQUEST, CatalogResponse.ResponseMessage.INCOMPATIBLE_SCHEMA, ex.getMessage()); } catch (UnsupportedSchemaTypeException ex) { LOG.error("Unsupported schema type encountered while adding schema [{}] with key [{}]", schemaVersion, schemaName, ex); response = WSUtils.respond(Response.Status.BAD_REQUEST, CatalogResponse.ResponseMessage.UNSUPPORTED_SCHEMA_TYPE, ex.getMessage()); } catch (Exception ex) { LOG.error("Encountered error while adding schema [{}] with key [{}]", schemaVersion, schemaName, ex, ex); response = WSUtils.respond(Response.Status.INTERNAL_SERVER_ERROR, CatalogResponse.ResponseMessage.EXCEPTION, ex.getMessage()); } return response; }); } @GET @Path("/schemas/{name}/versions/latest") @ApiOperation(value = "Get the latest version of the schema for the given schema name", response = SchemaVersionInfo.class, tags = OPERATION_GROUP_SCHEMA) @Timed public Response getLatestSchemaVersion(@ApiParam(value = "Schema name", required = true) @PathParam("name") String schemaName) { Response response; try { SchemaVersionInfo schemaVersionInfo = schemaRegistry.getLatestSchemaVersionInfo(schemaName); if (schemaVersionInfo != null) { response = WSUtils.respondEntity(schemaVersionInfo, Response.Status.OK); } else { LOG.info("No schemas found with schemakey: [{}]", schemaName); response = WSUtils.respond(Response.Status.NOT_FOUND, CatalogResponse.ResponseMessage.ENTITY_NOT_FOUND, schemaName); } } catch (Exception ex) { LOG.error("Encountered error while getting latest schema version for schemakey [{}]", schemaName, ex); response = WSUtils.respond(Response.Status.INTERNAL_SERVER_ERROR, CatalogResponse.ResponseMessage.EXCEPTION, ex.getMessage()); } return response; } @GET @Path("/schemas/{name}/versions") @ApiOperation(value = "Get all the versions of the schema for the given schema name)", response = SchemaVersionInfo.class, responseContainer = "List", tags = OPERATION_GROUP_SCHEMA) @Timed public Response getAllSchemaVersions(@ApiParam(value = "Schema name", required = true) @PathParam("name") String schemaName) { Response response; try { Collection<SchemaVersionInfo> schemaVersionInfos = schemaRegistry.findAllVersions(schemaName); if (schemaVersionInfos != null) { response = WSUtils.respondEntities(schemaVersionInfos, Response.Status.OK); } else { LOG.info("No schemas found with schemakey: [{}]", schemaName); response = WSUtils.respond(Response.Status.NOT_FOUND, CatalogResponse.ResponseMessage.ENTITY_NOT_FOUND, schemaName); } } catch (Exception ex) { LOG.error("Encountered error while getting all schema versions for schemakey [{}]", schemaName, ex); response = WSUtils.respond(Response.Status.INTERNAL_SERVER_ERROR, CatalogResponse.ResponseMessage.EXCEPTION, ex.getMessage()); } return response; } @GET @Path("/schemas/{name}/versions/{version}") @ApiOperation(value = "Get a version of the schema identified by the schema name", response = SchemaVersionInfo.class, tags = OPERATION_GROUP_SCHEMA) @Timed public Response getSchemaVersion(@ApiParam(value = "Schema name", required = true) @PathParam("name") String schemaMetadata, @ApiParam(value = "version of the schema", required = true) @PathParam("version") Integer version) { SchemaVersionKey schemaVersionKey = new SchemaVersionKey(schemaMetadata, version); Response response; try { SchemaVersionInfo schemaVersionInfo = schemaRegistry.getSchemaVersionInfo(schemaVersionKey); response = WSUtils.respondEntity(schemaVersionInfo, Response.Status.OK); } catch (SchemaNotFoundException e) { LOG.info("No schemas found with schemaVersionKey: [{}]", schemaVersionKey); response = WSUtils.respond(Response.Status.NOT_FOUND, CatalogResponse.ResponseMessage.ENTITY_NOT_FOUND, schemaVersionKey.toString()); } catch (Exception ex) { LOG.error("Encountered error while getting all schema versions for schemakey [{}]", schemaMetadata, ex); response = WSUtils.respond(Response.Status.INTERNAL_SERVER_ERROR, CatalogResponse.ResponseMessage.EXCEPTION, ex.getMessage()); } return response; } @POST @Path("/schemas/{name}/compatibility") @ApiOperation(value = "Checks if the given schema text is compatible with all the versions of the schema identified by the name", response = CompatibilityResult.class, tags = OPERATION_GROUP_SCHEMA) @Timed public Response checkCompatibilityWithSchema(@ApiParam(value = "Schema name", required = true) @PathParam("name") String schemaName, @ApiParam(value = "schema text", required = true) String schemaText) { Response response; try { CompatibilityResult compatible = schemaRegistry.checkCompatibility(schemaName, schemaText); response = WSUtils.respondEntity(compatible, Response.Status.OK); } catch (SchemaNotFoundException e) { LOG.error("No schemas found with schemakey: [{}]", schemaName, e); response = WSUtils.respond(Response.Status.NOT_FOUND, CatalogResponse.ResponseMessage.ENTITY_NOT_FOUND, schemaName); } catch (Exception ex) { LOG.error("Encountered error while checking compatibility with versions of schema with [{}] for given schema text [{}]", schemaName, schemaText, ex); response = WSUtils.respond(Response.Status.INTERNAL_SERVER_ERROR, CatalogResponse.ResponseMessage.EXCEPTION, ex.getMessage()); } return response; } @GET @Path("/schemas/{name}/serdes") @ApiOperation(value = "Get list of Serializers registered for the given schema name", response = SerDesInfo.class, responseContainer = "List", tags = OPERATION_GROUP_SERDE) @Timed public Response getSerializers(@ApiParam(value = "Schema name", required = true) @PathParam("name") String schemaName) { Response response; try { SchemaMetadataInfo schemaMetadataInfoStorable = schemaRegistry.getSchemaMetadata(schemaName); if (schemaMetadataInfoStorable != null) { Collection<SerDesInfo> schemaSerializers = schemaRegistry.getSchemaSerializers(schemaMetadataInfoStorable.getId()); response = WSUtils.respondEntities(schemaSerializers, Response.Status.OK); } else { LOG.info("No schemas found with schemakey: [{}]", schemaName); response = WSUtils.respond(Response.Status.NOT_FOUND, CatalogResponse.ResponseMessage.ENTITY_NOT_FOUND, schemaName); } } catch (Exception ex) { LOG.error("Encountered error while getting serializers for schemaKey [{}]", schemaName, ex); response = WSUtils.respond(Response.Status.INTERNAL_SERVER_ERROR, CatalogResponse.ResponseMessage.EXCEPTION, ex.getMessage()); } return response; } @POST @Consumes(MediaType.MULTIPART_FORM_DATA) @Path("/files") @ApiOperation(value = "Upload the given file and returns respective identifier.", response = String.class, tags = OPERATION_GROUP_OTHER) @Timed public Response uploadFile(@FormDataParam("file") final InputStream inputStream, @FormDataParam("file") final FormDataContentDisposition contentDispositionHeader) { Response response; try { LOG.info("Received contentDispositionHeader: [{}]", contentDispositionHeader); String uploadedFileId = schemaRegistry.uploadFile(inputStream); response = WSUtils.respondEntity(uploadedFileId, Response.Status.OK); } catch (Exception ex) { LOG.error("Encountered error while uploading file", ex); response = WSUtils.respond(Response.Status.INTERNAL_SERVER_ERROR, CatalogResponse.ResponseMessage.EXCEPTION, ex.getMessage()); } return response; } @GET @Produces({"application/octet-stream", "application/json"}) @Path("/files/download/{fileId}") @ApiOperation(value = "Downloads the respective for the given fileId if it exists", response = StreamingOutput.class, tags = OPERATION_GROUP_OTHER) @Timed public Response downloadFile(@ApiParam(value = "Identifier of the file to be downloaded", required = true) @PathParam("fileId") String fileId) { Response response; try { StreamingOutput streamOutput = WSUtils.wrapWithStreamingOutput(schemaRegistry.downloadFile(fileId)); response = Response.ok(streamOutput).build(); return response; } catch (FileNotFoundException e) { LOG.error("No file found for fileId [{}]", fileId, e); response = WSUtils.respondEntity(fileId, Response.Status.NOT_FOUND); } catch (Exception ex) { LOG.error("Encountered error while downloading file [{}]", fileId, ex); response = WSUtils.respond(Response.Status.INTERNAL_SERVER_ERROR, CatalogResponse.ResponseMessage.EXCEPTION, ex.getMessage()); } return response; } @POST @Path("/serdes") @ApiOperation(value = "Add a Serializer/Deserializer into the Schema Registry", response = Long.class, tags = OPERATION_GROUP_SERDE) @Timed public Response addSerializer(@ApiParam(value = "Serializer/Deserializer information to be registered", required = true) SerDesPair serDesPair, @Context UriInfo uriInfo) { return handleLeaderAction(uriInfo, () -> _addSerDesInfo(serDesPair)); } @GET @Path("/serdes/{id}") @ApiOperation(value = "Get a Serializer for the given serializer id", response = SerDesInfo.class, tags = OPERATION_GROUP_SERDE) @Timed public Response getSerializer(@ApiParam(value = "Serializer identifier", required = true) @PathParam("id") Long serializerId) { return _getSerDesInfo(serializerId); } private Response _addSerDesInfo(SerDesPair serDesInfo) { Response response; try { Long serializerId = schemaRegistry.addSerDesInfo(serDesInfo); response = WSUtils.respondEntity(serializerId, Response.Status.OK); } catch (Exception ex) { LOG.error("Encountered error while adding serializer/deserializer [{}]", serDesInfo, ex); response = WSUtils.respond(Response.Status.INTERNAL_SERVER_ERROR, CatalogResponse.ResponseMessage.EXCEPTION, ex.getMessage()); } return response; } private Response _getSerDesInfo(Long serializerId) { Response response; try { SerDesInfo serializerInfo = schemaRegistry.getSerDesInfo(serializerId); response = WSUtils.respondEntity(serializerInfo, Response.Status.OK); } catch (Exception ex) { LOG.error("Encountered error while getting serializer/deserializer [{}]", serializerId, ex); response = WSUtils.respond(Response.Status.INTERNAL_SERVER_ERROR, CatalogResponse.ResponseMessage.EXCEPTION, ex.getMessage()); } return response; } @POST @Path("/schemas/{name}/mapping/{serDesId}") @ApiOperation(value = "Bind the given Serializer/Deserializer to the schema identified by the schema name", tags = OPERATION_GROUP_SERDE) @Timed public Response mapSerDes(@ApiParam(value = "Schema name", required = true) @PathParam("name") String schemaName, @ApiParam(value = "Serializer/deserializer identifier", required = true) @PathParam("serDesId") Long serDesId, @Context UriInfo uriInfo) { return handleLeaderAction(uriInfo, () -> { Response response; try { SchemaMetadataInfo schemaMetadataInfoStorable = schemaRegistry.getSchemaMetadata(schemaName); schemaRegistry.mapSerDesWithSchema(schemaMetadataInfoStorable.getId(), serDesId); response = WSUtils.respondEntity(true, Response.Status.OK); } catch (Exception ex) { response = WSUtils.respond(Response.Status.INTERNAL_SERVER_ERROR, CatalogResponse.ResponseMessage.EXCEPTION, ex.getMessage()); } return response; }); } private static void checkValueAsNullOrEmpty(String name, String value) throws IllegalArgumentException { if (value == null) { throw new IllegalArgumentException("Parameter " + name + " is null"); } if (value.isEmpty()) { throw new IllegalArgumentException("Parameter " + name + " is empty"); } } }