/**
* Copyright (C) 2014-2016 LinkedIn Corp. (pinot-core@linkedin.com)
*
* 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 com.linkedin.pinot.common.utils;
import com.google.common.base.Preconditions;
import com.linkedin.pinot.common.data.FieldSpec;
import com.linkedin.pinot.common.data.Schema;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.DeleteMethod;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
import org.apache.commons.httpclient.methods.multipart.Part;
import org.apache.commons.httpclient.methods.multipart.StringPart;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.apache.helix.ZNRecord;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Utility class which contains {@link Schema} related operations.
*/
public class SchemaUtils {
private static final Logger LOGGER = LoggerFactory.getLogger(SchemaUtils.class);
private static final HttpClient HTTP_CLIENT = new HttpClient();
private SchemaUtils() {
}
/**
* Fetch {@link Schema} from a {@link ZNRecord}.
*/
public static Schema fromZNRecord(@Nonnull ZNRecord record)
throws IOException {
String schemaJSON = record.getSimpleField("schemaJSON");
return Schema.fromString(schemaJSON);
}
/**
* Wrap {@link Schema} into a {@link ZNRecord}.
*/
public static ZNRecord toZNRecord(@Nonnull Schema schema)
throws IllegalArgumentException, IllegalAccessException {
ZNRecord record = new ZNRecord(schema.getSchemaName());
record.setSimpleField("schemaJSON", schema.getJSONSchema());
return record;
}
/**
* Given host, port and schema name, send a http GET request to download the {@link Schema}.
*
* @return schema on success.
* <P><code>null</code> on failure.
*/
public static @Nullable Schema getSchema(@Nonnull String host, int port, @Nonnull String schemaName) {
Preconditions.checkNotNull(host);
Preconditions.checkNotNull(schemaName);
try {
URL url = new URL("http", host, port, "/schemas/" + schemaName);
GetMethod httpGet = new GetMethod(url.toString());
try {
int responseCode = HTTP_CLIENT.executeMethod(httpGet);
String response = httpGet.getResponseBodyAsString();
if (responseCode >= 400) {
// File not find error code.
if (responseCode == 404) {
LOGGER.info("Cannot find schema: {} from host: {}, port: {}", schemaName, host, port);
} else {
LOGGER.warn("Got error response code: {}, response: {}", responseCode, response);
}
return null;
}
return Schema.fromString(response);
} finally {
httpGet.releaseConnection();
}
} catch (Exception e) {
LOGGER.error("Caught exception while getting the schema: {} from host: {}, port: {}", schemaName, host, port, e);
return null;
}
}
/**
* Given host, port and schema, send a http POST request to upload the {@link Schema}.
*
* @return <code>true</code> on success.
* <P><code>false</code> on failure.
*/
public static boolean postSchema(@Nonnull String host, int port, @Nonnull Schema schema) {
Preconditions.checkNotNull(host);
Preconditions.checkNotNull(schema);
try {
URL url = new URL("http", host, port, "/schemas");
PostMethod httpPost = new PostMethod(url.toString());
try {
Part[] parts = {new StringPart(schema.getSchemaName(), schema.toString())};
MultipartRequestEntity requestEntity = new MultipartRequestEntity(parts, new HttpMethodParams());
httpPost.setRequestEntity(requestEntity);
int responseCode = HTTP_CLIENT.executeMethod(httpPost);
if (responseCode >= 400) {
String response = httpPost.getResponseBodyAsString();
LOGGER.warn("Got error response code: {}, response: {}", responseCode, response);
return false;
}
return true;
} finally {
httpPost.releaseConnection();
}
} catch (Exception e) {
LOGGER.error("Caught exception while posting the schema: {} to host: {}, port: {}", schema.getSchemaName(), host,
port, e);
return false;
}
}
/**
* Given host, port and schema name, send a http DELETE request to delete the {@link Schema}.
*
* @return <code>true</code> on success.
* <P><code>false</code> on failure.
*/
public static boolean deleteSchema(@Nonnull String host, int port, @Nonnull String schemaName) {
Preconditions.checkNotNull(host);
Preconditions.checkNotNull(schemaName);
try {
URL url = new URL("http", host, port, "/schemas/" + schemaName);
DeleteMethod httpDelete = new DeleteMethod(url.toString());
try {
int responseCode = HTTP_CLIENT.executeMethod(httpDelete);
if (responseCode >= 400) {
String response = httpDelete.getResponseBodyAsString();
LOGGER.warn("Got error response code: {}, response: {}", responseCode, response);
return false;
}
return true;
} finally {
httpDelete.releaseConnection();
}
} catch (Exception e) {
LOGGER.error("Caught exception while getting the schema: {} from host: {}, port: {}", schemaName, host, port, e);
return false;
}
}
/**
* Compare two schemas ignoring their version number.
*
* @return <code>true</code> if two schemas equal to each other.
* <p><code>false</code>if two schemas do not equal to each other.
*/
public static boolean equalsIgnoreVersion(@Nonnull Schema schema1, @Nonnull Schema schema2) {
Preconditions.checkNotNull(schema1);
Preconditions.checkNotNull(schema2);
return schema1.getSchemaName().equals(schema2.getSchemaName())
&& schema1.getFieldSpecMap().equals(schema2.getFieldSpecMap());
}
/**
* An example on how to use this utility class.
*/
public static void main(String[] args) {
Schema schema = new Schema.SchemaBuilder().setSchemaName("testSchema")
.addSingleValueDimension("dimension", FieldSpec.DataType.DOUBLE)
.addMetric("metric", FieldSpec.DataType.INT)
.addTime("time", TimeUnit.DAYS, FieldSpec.DataType.INT)
.build();
System.out.println(postSchema("localhost", 8100, schema));
Schema fetchedSchema = getSchema("localhost", 8100, "testSchema");
Preconditions.checkNotNull(fetchedSchema);
System.out.println(fetchedSchema);
System.out.println(equalsIgnoreVersion(schema, fetchedSchema));
System.out.println(deleteSchema("localhost", 8100, "testSchema"));
}
}