/** * 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.server.api.resources; import com.google.common.collect.ImmutableList; import com.linkedin.pinot.common.restlet.resources.TableSegments; import com.linkedin.pinot.common.restlet.resources.TablesList; import com.linkedin.pinot.core.data.manager.offline.InstanceDataManager; import com.linkedin.pinot.core.data.manager.offline.SegmentDataManager; import com.linkedin.pinot.core.data.manager.offline.TableDataManager; import com.linkedin.pinot.core.segment.index.SegmentMetadataImpl; import com.linkedin.pinot.server.starter.ServerInstance; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; import io.swagger.annotations.ApiResponse; import io.swagger.annotations.ApiResponses; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import javax.inject.Inject; import javax.ws.rs.DefaultValue; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import org.json.JSONException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @Api(tags = "Table") @Path("/") public class TablesResource { private static final Logger LOGGER = LoggerFactory.getLogger(TablesResource.class); @Inject ServerInstance serverInstance; @GET @Path("/tables") @Produces(MediaType.APPLICATION_JSON) //swagger annotations @ApiOperation(value = "List tables", notes = "List all the tables on this server") @ApiResponses(value = {@ApiResponse(code = 200, message = "Success", response = TablesList.class), @ApiResponse(code = 500, message = "Server initialization error", response = ErrorInfo.class)}) public TablesList listTables() { InstanceDataManager dataManager = checkGetInstanceDataManager(); Collection<TableDataManager> tableDataManagers = dataManager.getTableDataManagers(); List<String> tables = new ArrayList<>(tableDataManagers.size()); for (TableDataManager tableDataManager : tableDataManagers) { tables.add(tableDataManager.getTableName()); } return new TablesList(tables); } private InstanceDataManager checkGetInstanceDataManager() { if (serverInstance == null) { throw new WebApplicationException("Server initialization error. Missing server instance"); } InstanceDataManager dataManager = (InstanceDataManager) serverInstance.getInstanceDataManager(); if (dataManager == null) { throw new WebApplicationException("Server initialization error. Missing data manager", Response.Status.INTERNAL_SERVER_ERROR); } return dataManager; } private TableDataManager checkGetTableDataManager(String tableName) { InstanceDataManager dataManager = checkGetInstanceDataManager(); TableDataManager tableDataManager = dataManager.getTableDataManager(tableName); if (tableDataManager == null) { throw new WebApplicationException("Table " + tableName + " does not exist", Response.Status.NOT_FOUND); } return tableDataManager; } @GET @Path("/tables/{tableName}/segments") @Produces(MediaType.APPLICATION_JSON) @ApiOperation(value = "List table segments", notes = "List segments of table hosted on this server") @ApiResponses(value = {@ApiResponse(code = 200, message = "Success", response = TableSegments.class), @ApiResponse(code = 500, message = "Server initialization error", response = ErrorInfo.class)}) public TableSegments listTableSegments( @ApiParam(value = "Table name including type", required = true, example = "myTable_OFFLINE") @PathParam("tableName") String tableName) { TableDataManager tableDataManager = checkGetTableDataManager(tableName); ImmutableList<SegmentDataManager> segmentDataManagers = tableDataManager.acquireAllSegments(); List<String> segments = new ArrayList<>(segmentDataManagers.size()); for (SegmentDataManager segmentDataManager : segmentDataManagers) { segments.add(segmentDataManager.getSegmentName()); tableDataManager.releaseSegment(segmentDataManager); } return new TableSegments(segments); } @GET @Path("/tables/{tableName}/segments/{segmentName}/metadata") @Produces(MediaType.APPLICATION_JSON) @ApiOperation(value = "Provide segment metadata", notes = "Provide segments metadata for the segment on server") @ApiResponses( value = { @ApiResponse(code=200, message = "Success"), @ApiResponse(code=500, message = "Internal server error", response = ErrorInfo.class), @ApiResponse(code = 404, message = "Table or segment not found", response = ErrorInfo.class) }) public String getSegmentMetadata( @ApiParam(value = "Table name including type", required = true, example = "myTable_OFFLINE") @PathParam("tableName") String tableName, @ApiParam(value = "Segment Name", required = true) @PathParam("segmentName") String segmentName, @ApiParam(value = "column name", required = false, allowMultiple = true, defaultValue = "") @QueryParam("columns") @DefaultValue("") List<String> columns ) { TableDataManager tableDataManager = checkGetTableDataManager(tableName); SegmentDataManager segmentDataManager = null; try { segmentDataManager = tableDataManager.acquireSegment(segmentName); if (segmentDataManager == null) { throw new WebApplicationException(String.format("Table %s segments %s does not exist", tableName, segmentName), Response.Status.NOT_FOUND); } SegmentMetadataImpl segmentMetadata = (SegmentMetadataImpl) segmentDataManager.getSegment().getSegmentMetadata(); Set<String> columnSet; if (columns.size() == 1 && columns.get(0).equals("*")) { columnSet = null; } else { columnSet = new HashSet<>(columns); } try { return segmentMetadata.toJson(columnSet).toString(); } catch (JSONException e) { LOGGER.error("Failed to convert table {} segment {} to json", tableName, segmentMetadata); throw new WebApplicationException("Failed to convert segment metadata to json", Response.Status.INTERNAL_SERVER_ERROR); } } finally { if (segmentDataManager != null) { tableDataManager.releaseSegment(segmentDataManager); } } } }