/**
* Copyright 2014 Confluent Inc.
*
* 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
*
* 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 io.confluent.kafka.schemaregistry.rest.resources;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.validation.constraints.NotNull;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.container.AsyncResponse;
import javax.ws.rs.container.Suspended;
import io.confluent.kafka.schemaregistry.client.rest.Versions;
import io.confluent.kafka.schemaregistry.client.rest.entities.Schema;
import io.confluent.kafka.schemaregistry.client.rest.entities.requests.RegisterSchemaRequest;
import io.confluent.kafka.schemaregistry.client.rest.entities.requests.RegisterSchemaResponse;
import io.confluent.kafka.schemaregistry.exceptions.IncompatibleSchemaException;
import io.confluent.kafka.schemaregistry.exceptions.InvalidSchemaException;
import io.confluent.kafka.schemaregistry.exceptions.InvalidVersionException;
import io.confluent.kafka.schemaregistry.exceptions.SchemaRegistryException;
import io.confluent.kafka.schemaregistry.exceptions.SchemaRegistryRequestForwardingException;
import io.confluent.kafka.schemaregistry.exceptions.SchemaRegistryStoreException;
import io.confluent.kafka.schemaregistry.exceptions.SchemaRegistryTimeoutException;
import io.confluent.kafka.schemaregistry.exceptions.UnknownMasterException;
import io.confluent.kafka.schemaregistry.rest.VersionId;
import io.confluent.kafka.schemaregistry.rest.exceptions.Errors;
import io.confluent.kafka.schemaregistry.storage.KafkaSchemaRegistry;
import io.confluent.rest.annotations.PerformanceMetric;
@Path("/subjects/{subject}/versions")
@Produces({Versions.SCHEMA_REGISTRY_V1_JSON_WEIGHTED,
Versions.SCHEMA_REGISTRY_DEFAULT_JSON_WEIGHTED,
Versions.JSON_WEIGHTED})
@Consumes({Versions.SCHEMA_REGISTRY_V1_JSON,
Versions.SCHEMA_REGISTRY_DEFAULT_JSON,
Versions.JSON, Versions.GENERIC_REQUEST})
public class SubjectVersionsResource {
private static final Logger log = LoggerFactory.getLogger(SubjectVersionsResource.class);
private final KafkaSchemaRegistry schemaRegistry;
public SubjectVersionsResource(KafkaSchemaRegistry registry) {
this.schemaRegistry = registry;
}
@GET
@Path("/{version}")
@PerformanceMetric("subjects.versions.get-schema")
public Schema getSchema(@PathParam("subject") String subject,
@PathParam("version") String version) {
VersionId versionId = null;
try {
versionId = new VersionId(version);
} catch (InvalidVersionException e) {
throw Errors.invalidVersionException();
}
Schema schema = null;
String errorMessage = null;
try {
schema = schemaRegistry.validateAndGetSchema(subject, versionId, false);
} catch (SchemaRegistryStoreException e) {
errorMessage =
"Error while retrieving schema for subject "
+ subject
+ " with version "
+ version
+ " from the schema registry";
log.debug(errorMessage, e);
throw Errors.storeException(errorMessage, e);
} catch (InvalidVersionException e) {
throw Errors.invalidVersionException();
} catch (SchemaRegistryException e) {
throw Errors.schemaRegistryException(errorMessage, e);
}
return schema;
}
@GET
@PerformanceMetric("subjects.versions.list")
public List<Integer> list(@PathParam("subject") String subject) {
// check if subject exists. If not, throw 404
Iterator<Schema> allSchemasForThisTopic = null;
List<Integer> allVersions = new ArrayList<Integer>();
String errorMessage = "Error while validating that subject "
+ subject
+ " exists in the registry";
try {
if (!schemaRegistry.listSubjects().contains(subject)) {
throw Errors.subjectNotFoundException();
}
} catch (SchemaRegistryStoreException e) {
throw Errors.storeException(errorMessage, e);
} catch (SchemaRegistryException e) {
throw Errors.schemaRegistryException(errorMessage, e);
}
errorMessage = "Error while listing all versions for subject "
+ subject;
try {
//return only non-deleted versions for the subject
allSchemasForThisTopic = schemaRegistry.getAllVersions(subject, false);
} catch (SchemaRegistryStoreException e) {
throw Errors.storeException(errorMessage, e);
} catch (SchemaRegistryException e) {
throw Errors.schemaRegistryException(errorMessage, e);
}
while (allSchemasForThisTopic.hasNext()) {
Schema schema = allSchemasForThisTopic.next();
allVersions.add(schema.getVersion());
}
return allVersions;
}
@POST
@PerformanceMetric("subjects.versions.register")
public void register(final @Suspended AsyncResponse asyncResponse,
final @HeaderParam("Content-Type") String contentType,
final @HeaderParam("Accept") String accept,
@PathParam("subject") String subjectName,
@NotNull RegisterSchemaRequest request) {
Map<String, String> headerProperties = new HashMap<String, String>();
headerProperties.put("Content-Type", contentType);
headerProperties.put("Accept", accept);
Schema schema = new Schema(subjectName, 0, 0, request.getSchema());
int id = 0;
try {
id = schemaRegistry.registerOrForward(subjectName, schema, headerProperties);
} catch (InvalidSchemaException e) {
throw Errors.invalidAvroException("Input schema is an invalid Avro schema", e);
} catch (SchemaRegistryTimeoutException e) {
throw Errors.operationTimeoutException("Register operation timed out", e);
} catch (SchemaRegistryStoreException e) {
throw Errors.storeException("Register schema operation failed while writing"
+ " to the Kafka store", e);
} catch (SchemaRegistryRequestForwardingException e) {
throw Errors.requestForwardingFailedException("Error while forwarding register schema request"
+ " to the master", e);
} catch (IncompatibleSchemaException e) {
throw Errors.incompatibleSchemaException("Schema being registered is incompatible with an"
+ " earlier schema", e);
} catch (UnknownMasterException e) {
throw Errors.unknownMasterException("Master not known.", e);
} catch (SchemaRegistryException e) {
throw Errors.schemaRegistryException("Error while registering schema", e);
}
RegisterSchemaResponse registerSchemaResponse = new RegisterSchemaResponse();
registerSchemaResponse.setId(id);
asyncResponse.resume(registerSchemaResponse);
}
@DELETE
@Path("/{version}")
@PerformanceMetric("subjects.versions.deleteSchemaVersion-schema")
public void deleteSchemaVersion(final @Suspended AsyncResponse asyncResponse,
@PathParam("subject") String subject,
@PathParam("version") String version) {
VersionId versionId = null;
try {
versionId = new VersionId(version);
} catch (InvalidVersionException e) {
throw Errors.invalidVersionException();
}
Schema schema = null;
String errorMessage = null;
try {
schema = schemaRegistry.validateAndGetSchema(subject, versionId, false);
} catch (SchemaRegistryStoreException e) {
errorMessage =
"Error while retrieving schema for subject "
+ subject
+ " with version "
+ version
+ " from the schema registry";
log.debug(errorMessage, e);
throw Errors.storeException(errorMessage, e);
} catch (InvalidVersionException e) {
throw Errors.invalidVersionException();
} catch (SchemaRegistryException e) {
throw Errors.schemaRegistryException(errorMessage, e);
}
try {
schemaRegistry.deleteSchemaVersionOrForward(subject, schema);
} catch (SchemaRegistryTimeoutException e) {
throw Errors.operationTimeoutException("Delete Schema Version operation timed out", e);
} catch (SchemaRegistryStoreException e) {
throw Errors.storeException("Delete Schema Version operation failed while writing"
+ " to the Kafka store", e);
} catch (SchemaRegistryRequestForwardingException e) {
throw Errors
.requestForwardingFailedException("Error while forwarding delete schema version request"
+ " to the master", e);
} catch (UnknownMasterException e) {
throw Errors.unknownMasterException("Master not known.", e);
} catch (SchemaRegistryException e) {
throw Errors.schemaRegistryException("Error while deleting Schema Version", e);
}
asyncResponse.resume(schema.getVersion());
}
}