http://git-wip-us.apache.org/repos/asf/kylin/blob/1a124e68/server-base/src/main/java/org/apache/kylin/rest/controller/CubeController.java ---------------------------------------------------------------------- diff --git a/server-base/src/main/java/org/apache/kylin/rest/controller/CubeController.java b/server-base/src/main/java/org/apache/kylin/rest/controller/CubeController.java new file mode 100644 index 0000000..9315a20 --- /dev/null +++ b/server-base/src/main/java/org/apache/kylin/rest/controller/CubeController.java @@ -0,0 +1,615 @@ +/* + * 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.kylin.rest.controller; + +import java.io.IOException; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; + +import org.apache.commons.lang.StringUtils; +import org.apache.kylin.common.util.JsonUtil; +import org.apache.kylin.cube.CubeInstance; +import org.apache.kylin.cube.CubeSegment; +import org.apache.kylin.cube.model.CubeBuildTypeEnum; +import org.apache.kylin.cube.model.CubeDesc; +import org.apache.kylin.cube.model.CubeJoinedFlatTableDesc; +import org.apache.kylin.dimension.DimensionEncodingFactory; +import org.apache.kylin.engine.streaming.StreamingConfig; +import org.apache.kylin.job.JobInstance; +import org.apache.kylin.job.JoinedFlatTable; +import org.apache.kylin.metadata.model.SegmentStatusEnum; +import org.apache.kylin.metadata.project.ProjectInstance; +import org.apache.kylin.rest.exception.BadRequestException; +import org.apache.kylin.rest.exception.ForbiddenException; +import org.apache.kylin.rest.exception.InternalErrorException; +import org.apache.kylin.rest.exception.NotFoundException; +import org.apache.kylin.rest.request.CubeRequest; +import org.apache.kylin.rest.request.JobBuildRequest; +import org.apache.kylin.rest.request.JobBuildRequest2; +import org.apache.kylin.rest.response.GeneralResponse; +import org.apache.kylin.rest.response.HBaseResponse; +import org.apache.kylin.rest.service.CubeService; +import org.apache.kylin.rest.service.JobService; +import org.apache.kylin.rest.service.KafkaConfigService; +import org.apache.kylin.rest.service.StreamingService; +import org.apache.kylin.source.kafka.config.KafkaConfig; +import org.apache.kylin.storage.hbase.cube.v1.coprocessor.observer.ObserverEnabler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonMappingException; + +import com.google.common.collect.Sets; + +/** + * CubeController is defined as Restful API entrance for UI. + */ +@Controller +@RequestMapping(value = "/cubes") +public class CubeController extends BasicController { + private static final Logger logger = LoggerFactory.getLogger(CubeController.class); + + @Autowired + private StreamingService streamingService; + + @Autowired + private KafkaConfigService kafkaConfigService; + + @Autowired + private CubeService cubeService; + + @Autowired + private JobService jobService; + + @RequestMapping(value = "", method = { RequestMethod.GET }) + @ResponseBody + public List<CubeInstance> getCubes(@RequestParam(value = "cubeName", required = false) String cubeName, @RequestParam(value = "modelName", required = false) String modelName, @RequestParam(value = "projectName", required = false) String projectName, @RequestParam(value = "limit", required = false) Integer limit, @RequestParam(value = "offset", required = false) Integer offset) { + return cubeService.getCubes(cubeName, projectName, modelName, limit, offset); + } + + @RequestMapping(value = "validEncodings", method = { RequestMethod.GET }) + @ResponseBody + public Set<String> getValidEncodings() { + Set<String> encodings; + try { + encodings = DimensionEncodingFactory.getValidEncodings(); + } catch (Exception e) { + return Sets.newTreeSet(); + } + return encodings; + } + + @RequestMapping(value = "/{cubeName}", method = { RequestMethod.GET }) + @ResponseBody + public CubeInstance getCube(@PathVariable String cubeName) { + CubeInstance cube = cubeService.getCubeManager().getCube(cubeName); + if (cube == null) { + throw new InternalErrorException("Cannot find cube " + cubeName); + } + return cube; + } + + /** + * Get hive SQL of the cube + * + * @param cubeName Cube Name + * @return + * @throws UnknownHostException + * @throws IOException + */ + @RequestMapping(value = "/{cubeName}/segs/{segmentName}/sql", method = { RequestMethod.GET }) + @ResponseBody + public GeneralResponse getSql(@PathVariable String cubeName, @PathVariable String segmentName) { + CubeInstance cube = cubeService.getCubeManager().getCube(cubeName); + CubeDesc cubeDesc = cube.getDescriptor(); + CubeSegment cubeSegment = cube.getSegment(segmentName, SegmentStatusEnum.READY); + CubeJoinedFlatTableDesc flatTableDesc = new CubeJoinedFlatTableDesc(cubeDesc, cubeSegment); + String sql = JoinedFlatTable.generateSelectDataStatement(flatTableDesc); + + GeneralResponse repsonse = new GeneralResponse(); + repsonse.setProperty("sql", sql); + + return repsonse; + } + + /** + * Update cube notify list + * + * @param cubeName + * @param notifyList + * @throws IOException + */ + @RequestMapping(value = "/{cubeName}/notify_list", method = { RequestMethod.PUT }) + @ResponseBody + public void updateNotifyList(@PathVariable String cubeName, @RequestBody List<String> notifyList) { + CubeInstance cube = cubeService.getCubeManager().getCube(cubeName); + + if (cube == null) { + throw new InternalErrorException("Cannot find cube " + cubeName); + } + + try { + cubeService.updateCubeNotifyList(cube, notifyList); + } catch (Exception e) { + logger.error(e.getLocalizedMessage(), e); + throw new InternalErrorException(e.getLocalizedMessage()); + } + + } + + @RequestMapping(value = "/{cubeName}/cost", method = { RequestMethod.PUT }) + @ResponseBody + public CubeInstance updateCubeCost(@PathVariable String cubeName, @RequestParam(value = "cost") int cost) { + try { + return cubeService.updateCubeCost(cubeName, cost); + } catch (Exception e) { + String message = "Failed to update cube cost: " + cubeName + " : " + cost; + logger.error(message, e); + throw new InternalErrorException(message + " Caused by: " + e.getMessage(), e); + } + } + + @RequestMapping(value = "/{cubeName}/coprocessor", method = { RequestMethod.PUT }) + @ResponseBody + public Map<String, Boolean> updateCubeCoprocessor(@PathVariable String cubeName, @RequestParam(value = "force") String force) { + try { + ObserverEnabler.updateCubeOverride(cubeName, force); + return ObserverEnabler.getCubeOverrides(); + } catch (Exception e) { + String message = "Failed to update cube coprocessor: " + cubeName + " : " + force; + logger.error(message, e); + throw new InternalErrorException(message + " Caused by: " + e.getMessage(), e); + } + } + + /** + * Force rebuild a cube's lookup table snapshot + * + * @throws IOException + */ + @RequestMapping(value = "/{cubeName}/segs/{segmentName}/refresh_lookup", method = { RequestMethod.PUT }) + @ResponseBody + public CubeInstance rebuildLookupSnapshot(@PathVariable String cubeName, @PathVariable String segmentName, @RequestParam(value = "lookupTable") String lookupTable) { + try { + return cubeService.rebuildLookupSnapshot(cubeName, segmentName, lookupTable); + } catch (IOException e) { + logger.error(e.getLocalizedMessage(), e); + throw new InternalErrorException(e.getLocalizedMessage()); + } + } + + /** + * Delete a cube segment + * + * @throws IOException + */ + @RequestMapping(value = "/{cubeName}/segs/{segmentName}", method = { RequestMethod.DELETE }) + @ResponseBody + public CubeInstance deleteSegment(@PathVariable String cubeName, @PathVariable String segmentName) { + CubeInstance cube = cubeService.getCubeManager().getCube(cubeName); + + if (cube == null) { + throw new InternalErrorException("Cannot find cube " + cubeName); + } + + CubeSegment segment = cube.getSegment(segmentName, null); + if (segment == null) { + throw new InternalErrorException("Cannot find segment '" + segmentName + "'"); + } + + try { + return cubeService.deleteSegment(cube, segmentName); + } catch (Exception e) { + logger.error(e.getLocalizedMessage(), e); + throw new InternalErrorException(e.getLocalizedMessage()); + } + } + + /** Build/Rebuild a cube segment */ + @RequestMapping(value = "/{cubeName}/build", method = { RequestMethod.PUT }) + @ResponseBody + public JobInstance build(@PathVariable String cubeName, @RequestBody JobBuildRequest req) { + return rebuild(cubeName, req); + } + + /** Build/Rebuild a cube segment */ + @RequestMapping(value = "/{cubeName}/rebuild", method = { RequestMethod.PUT }) + @ResponseBody + public JobInstance rebuild(@PathVariable String cubeName, @RequestBody JobBuildRequest req) { + return buildInternal(cubeName, req.getStartTime(), req.getEndTime(), 0, 0, req.getBuildType(), req.isForce() || req.isForceMergeEmptySegment()); + } + + /** Build/Rebuild a cube segment by source offset */ + @RequestMapping(value = "/{cubeName}/build2", method = { RequestMethod.PUT }) + @ResponseBody + public JobInstance build(@PathVariable String cubeName, @RequestBody JobBuildRequest2 req) { + return rebuild(cubeName, req); + } + + /** Build/Rebuild a cube segment by source offset */ + @RequestMapping(value = "/{cubeName}/rebuild2", method = { RequestMethod.PUT }) + @ResponseBody + public JobInstance rebuild(@PathVariable String cubeName, @RequestBody JobBuildRequest2 req) { + return buildInternal(cubeName, 0, 0, req.getStartSourceOffset(), req.getEndSourceOffset(), req.getBuildType(), req.isForce()); + } + + private JobInstance buildInternal(String cubeName, long startTime, long endTime, // + long startOffset, long endOffset, String buildType, boolean force) { + try { + String submitter = SecurityContextHolder.getContext().getAuthentication().getName(); + CubeInstance cube = jobService.getCubeManager().getCube(cubeName); + return jobService.submitJob(cube, startTime, endTime, startOffset, endOffset, // + CubeBuildTypeEnum.valueOf(buildType), force, submitter); + } catch (Exception e) { + logger.error(e.getLocalizedMessage(), e); + throw new InternalErrorException(e.getLocalizedMessage()); + } + } + + @RequestMapping(value = "/{cubeName}/disable", method = { RequestMethod.PUT }) + @ResponseBody + public CubeInstance disableCube(@PathVariable String cubeName) { + try { + CubeInstance cube = cubeService.getCubeManager().getCube(cubeName); + + if (cube == null) { + throw new InternalErrorException("Cannot find cube " + cubeName); + } + + return cubeService.disableCube(cube); + } catch (Exception e) { + String message = "Failed to disable cube: " + cubeName; + logger.error(message, e); + throw new InternalErrorException(message + " Caused by: " + e.getMessage(), e); + } + } + + @RequestMapping(value = "/{cubeName}/purge", method = { RequestMethod.PUT }) + @ResponseBody + public CubeInstance purgeCube(@PathVariable String cubeName) { + try { + CubeInstance cube = cubeService.getCubeManager().getCube(cubeName); + + if (cube == null) { + throw new InternalErrorException("Cannot find cube " + cubeName); + } + + return cubeService.purgeCube(cube); + } catch (Exception e) { + String message = "Failed to purge cube: " + cubeName; + logger.error(message, e); + throw new InternalErrorException(message + " Caused by: " + e.getMessage(), e); + } + } + + @RequestMapping(value = "/{cubeName}/clone", method = { RequestMethod.PUT }) + @ResponseBody + public CubeInstance cloneCube(@PathVariable String cubeName, @RequestBody CubeRequest cubeRequest) { + String newCubeName = cubeRequest.getCubeName(); + String project = cubeRequest.getProject(); + + CubeInstance cube = cubeService.getCubeManager().getCube(cubeName); + if (cube == null) { + throw new InternalErrorException("Cannot find cube " + cubeName); + } + CubeDesc cubeDesc = cube.getDescriptor(); + CubeDesc newCubeDesc = CubeDesc.getCopyOf(cubeDesc); + newCubeDesc.setName(newCubeName); + + CubeInstance newCube; + try { + newCube = cubeService.createCubeAndDesc(newCubeName, project, newCubeDesc); + + //reload to avoid shallow clone + cubeService.getCubeDescManager().reloadCubeDescLocal(newCubeName); + } catch (IOException e) { + throw new InternalErrorException("Failed to clone cube ", e); + } + + return newCube; + + } + + @RequestMapping(value = "/{cubeName}/enable", method = { RequestMethod.PUT }) + @ResponseBody + public CubeInstance enableCube(@PathVariable String cubeName) { + try { + CubeInstance cube = cubeService.getCubeManager().getCube(cubeName); + if (null == cube) { + throw new InternalErrorException("Cannot find cube " + cubeName); + } + + return cubeService.enableCube(cube); + } catch (Exception e) { + String message = "Failed to enable cube: " + cubeName; + logger.error(message, e); + throw new InternalErrorException(message + " Caused by: " + e.getMessage(), e); + } + } + + @RequestMapping(value = "/{cubeName}", method = { RequestMethod.DELETE }) + @ResponseBody + public void deleteCube(@PathVariable String cubeName) { + CubeInstance cube = cubeService.getCubeManager().getCube(cubeName); + if (null == cube) { + throw new NotFoundException("Cube with name " + cubeName + " not found.."); + } + + //drop Cube + try { + cubeService.deleteCube(cube); + } catch (Exception e) { + logger.error(e.getLocalizedMessage(), e); + throw new InternalErrorException("Failed to delete cube. " + " Caused by: " + e.getMessage(), e); + } + + } + + /** + * save cubeDesc + * + * @return Table metadata array + * @throws IOException + */ + @RequestMapping(value = "", method = { RequestMethod.POST }) + @ResponseBody + public CubeRequest saveCubeDesc(@RequestBody CubeRequest cubeRequest) { + + CubeDesc desc = deserializeCubeDesc(cubeRequest); + if (desc == null) { + cubeRequest.setMessage("CubeDesc is null."); + return cubeRequest; + } + String name = CubeService.getCubeNameFromDesc(desc.getName()); + if (StringUtils.isEmpty(name)) { + logger.info("Cube name should not be empty."); + throw new BadRequestException("Cube name should not be empty."); + } + + try { + desc.setUuid(UUID.randomUUID().toString()); + String projectName = (null == cubeRequest.getProject()) ? ProjectInstance.DEFAULT_PROJECT_NAME : cubeRequest.getProject(); + cubeService.createCubeAndDesc(name, projectName, desc); + } catch (Exception e) { + logger.error("Failed to deal with the request.", e); + throw new InternalErrorException(e.getLocalizedMessage(), e); + } + + cubeRequest.setUuid(desc.getUuid()); + cubeRequest.setSuccessful(true); + return cubeRequest; + } + + /** + * update CubDesc + * + * @return Table metadata array + * @throws JsonProcessingException + * @throws IOException + */ + @RequestMapping(value = "", method = { RequestMethod.PUT }) + @ResponseBody + public CubeRequest updateCubeDesc(@RequestBody CubeRequest cubeRequest) throws JsonProcessingException { + + //update cube + CubeDesc desc = deserializeCubeDesc(cubeRequest); + CubeDesc oldCubeDesc; + boolean isCubeDescFreeEditable; + + if (desc == null) { + return cubeRequest; + } + + // Check if the cube is editable + isCubeDescFreeEditable = cubeService.isCubeDescFreeEditable(desc); + + String projectName = (null == cubeRequest.getProject()) ? ProjectInstance.DEFAULT_PROJECT_NAME : cubeRequest.getProject(); + try { + CubeInstance cube = cubeService.getCubeManager().getCube(cubeRequest.getCubeName()); + + if (cube == null) { + String error = "The cube named " + cubeRequest.getCubeName() + " does not exist "; + updateRequest(cubeRequest, false, error); + return cubeRequest; + } + + //cube renaming is not allowed + if (!cube.getDescriptor().getName().equalsIgnoreCase(desc.getName())) { + String error = "Cube Desc renaming is not allowed: desc.getName(): " + desc.getName() + ", cubeRequest.getCubeName(): " + cubeRequest.getCubeName(); + updateRequest(cubeRequest, false, error); + return cubeRequest; + } + + oldCubeDesc = cube.getDescriptor(); + if (isCubeDescFreeEditable || oldCubeDesc.consistentWith(desc)) { + desc = cubeService.updateCubeAndDesc(cube, desc, projectName, true); + } else { + logger.warn("Won't update the cube desc due to inconsistency"); + updateRequest(cubeRequest, false, "CubeDesc " + desc.getName() + " is inconsistent with existing. Try purge that cube first or avoid updating key cube desc fields."); + return cubeRequest; + } + } catch (AccessDeniedException accessDeniedException) { + throw new ForbiddenException("You don't have right to update this cube."); + } catch (Exception e) { + logger.error("Failed to deal with the request:" + e.getLocalizedMessage(), e); + throw new InternalErrorException("Failed to deal with the request: " + e.getLocalizedMessage()); + } + + if (!desc.getError().isEmpty()) { + logger.warn("Cube " + desc.getName() + " fail to update because " + desc.getError()); + updateRequest(cubeRequest, false, omitMessage(desc.getError())); + return cubeRequest; + } + + String descData = JsonUtil.writeValueAsIndentString(desc); + cubeRequest.setCubeDescData(descData); + cubeRequest.setSuccessful(true); + return cubeRequest; + } + + /** + * get Hbase Info + * + * @return true + * @throws IOException + */ + @RequestMapping(value = "/{cubeName}/hbase", method = { RequestMethod.GET }) + @ResponseBody + public List<HBaseResponse> getHBaseInfo(@PathVariable String cubeName) { + List<HBaseResponse> hbase = new ArrayList<HBaseResponse>(); + + CubeInstance cube = cubeService.getCubeManager().getCube(cubeName); + if (null == cube) { + throw new InternalErrorException("Cannot find cube " + cubeName); + } + + List<CubeSegment> segments = cube.getSegments(); + + for (CubeSegment segment : segments) { + String tableName = segment.getStorageLocationIdentifier(); + HBaseResponse hr = null; + + // Get info of given table. + try { + hr = cubeService.getHTableInfo(tableName); + } catch (IOException e) { + logger.error("Failed to calcuate size of HTable \"" + tableName + "\".", e); + } + + if (null == hr) { + logger.info("Failed to calcuate size of HTable \"" + tableName + "\"."); + hr = new HBaseResponse(); + } + + hr.setTableName(tableName); + hr.setDateRangeStart(segment.getDateRangeStart()); + hr.setDateRangeEnd(segment.getDateRangeEnd()); + hbase.add(hr); + } + + return hbase; + } + + private CubeDesc deserializeCubeDesc(CubeRequest cubeRequest) { + CubeDesc desc = null; + try { + logger.debug("Saving cube " + cubeRequest.getCubeDescData()); + desc = JsonUtil.readValue(cubeRequest.getCubeDescData(), CubeDesc.class); + } catch (JsonParseException e) { + logger.error("The cube definition is not valid.", e); + updateRequest(cubeRequest, false, e.getMessage()); + } catch (JsonMappingException e) { + logger.error("The cube definition is not valid.", e); + updateRequest(cubeRequest, false, e.getMessage()); + } catch (IOException e) { + logger.error("Failed to deal with the request.", e); + throw new InternalErrorException("Failed to deal with the request:" + e.getMessage(), e); + } + return desc; + } + + private StreamingConfig deserializeStreamingDesc(CubeRequest cubeRequest) { + StreamingConfig desc = null; + try { + logger.debug("Saving StreamingConfig " + cubeRequest.getStreamingData()); + desc = JsonUtil.readValue(cubeRequest.getStreamingData(), StreamingConfig.class); + } catch (JsonParseException e) { + logger.error("The StreamingConfig definition is not valid.", e); + updateRequest(cubeRequest, false, e.getMessage()); + } catch (JsonMappingException e) { + logger.error("The data StreamingConfig definition is not valid.", e); + updateRequest(cubeRequest, false, e.getMessage()); + } catch (IOException e) { + logger.error("Failed to deal with the request.", e); + throw new InternalErrorException("Failed to deal with the request:" + e.getMessage(), e); + } + return desc; + } + + private KafkaConfig deserializeKafkaDesc(CubeRequest cubeRequest) { + KafkaConfig desc = null; + try { + logger.debug("Saving KafkaConfig " + cubeRequest.getKafkaData()); + desc = JsonUtil.readValue(cubeRequest.getKafkaData(), KafkaConfig.class); + } catch (JsonParseException e) { + logger.error("The KafkaConfig definition is not valid.", e); + updateRequest(cubeRequest, false, e.getMessage()); + } catch (JsonMappingException e) { + logger.error("The data KafkaConfig definition is not valid.", e); + updateRequest(cubeRequest, false, e.getMessage()); + } catch (IOException e) { + logger.error("Failed to deal with the request.", e); + throw new InternalErrorException("Failed to deal with the request:" + e.getMessage(), e); + } + return desc; + } + + /** + * @return + */ + private String omitMessage(List<String> errors) { + StringBuffer buffer = new StringBuffer(); + for (Iterator<String> iterator = errors.iterator(); iterator.hasNext();) { + String string = (String) iterator.next(); + buffer.append(string); + buffer.append("\n"); + } + return buffer.toString(); + } + + private void updateRequest(CubeRequest request, boolean success, String message) { + request.setCubeDescData(""); + request.setSuccessful(success); + request.setMessage(message); + } + + public void setCubeService(CubeService cubeService) { + this.cubeService = cubeService; + } + + public void setJobService(JobService jobService) { + this.jobService = jobService; + } + + public void setStreamingService(StreamingService streamingService) { + this.streamingService = streamingService; + } + + public void setKafkaConfigService(KafkaConfigService kafkaConfigService) { + this.kafkaConfigService = kafkaConfigService; + } + +}
http://git-wip-us.apache.org/repos/asf/kylin/blob/1a124e68/server-base/src/main/java/org/apache/kylin/rest/controller/CubeDescController.java ---------------------------------------------------------------------- diff --git a/server-base/src/main/java/org/apache/kylin/rest/controller/CubeDescController.java b/server-base/src/main/java/org/apache/kylin/rest/controller/CubeDescController.java new file mode 100644 index 0000000..61584a3 --- /dev/null +++ b/server-base/src/main/java/org/apache/kylin/rest/controller/CubeDescController.java @@ -0,0 +1,95 @@ +/* + * 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.kylin.rest.controller; + +import java.io.IOException; + +import org.apache.kylin.cube.CubeInstance; +import org.apache.kylin.cube.model.CubeDesc; +import org.apache.kylin.rest.service.CubeService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; + +/** + * @author xduo + * + */ +@Controller +@RequestMapping(value = "/cube_desc") +public class CubeDescController { + + @Autowired + private CubeService cubeService; + + /** + * Get detail information of the "Cube ID" + * + * @param cubeDescName + * Cube ID + * @return + * @throws IOException + */ + @RequestMapping(value = "/{cubeName}", method = { RequestMethod.GET }) + @ResponseBody + public CubeDesc[] getCube(@PathVariable String cubeName) { + CubeInstance cubeInstance = cubeService.getCubeManager().getCube(cubeName); + if (cubeInstance == null) { + return null; + } + CubeDesc cSchema = cubeInstance.getDescriptor(); + if (cSchema != null) { + return new CubeDesc[] { cSchema }; + } else { + return null; + } + } + + /** + * Get detail information of the "Cube ID" + * return CubeDesc instead of CubeDesc[] + * + * @param cubeDescName + * Cube ID + * @return + * @throws IOException + */ + @RequestMapping(value = "/{cubeName}/desc", method = { RequestMethod.GET }) + @ResponseBody + public CubeDesc getDesc(@PathVariable String cubeName) { + CubeInstance cubeInstance = cubeService.getCubeManager().getCube(cubeName); + if (cubeInstance == null) { + return null; + } + CubeDesc cSchema = cubeInstance.getDescriptor(); + if (cSchema != null) { + return cSchema; + } else { + return null; + } + } + + public void setCubeService(CubeService cubeService) { + this.cubeService = cubeService; + } + +} http://git-wip-us.apache.org/repos/asf/kylin/blob/1a124e68/server-base/src/main/java/org/apache/kylin/rest/controller/DiagnosisController.java ---------------------------------------------------------------------- diff --git a/server-base/src/main/java/org/apache/kylin/rest/controller/DiagnosisController.java b/server-base/src/main/java/org/apache/kylin/rest/controller/DiagnosisController.java new file mode 100644 index 0000000..b9da9b2 --- /dev/null +++ b/server-base/src/main/java/org/apache/kylin/rest/controller/DiagnosisController.java @@ -0,0 +1,120 @@ +/* + * 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.kylin.rest.controller; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.List; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.io.IOUtils; +import org.apache.kylin.metadata.badquery.BadQueryEntry; +import org.apache.kylin.metadata.badquery.BadQueryHistory; +import org.apache.kylin.rest.exception.InternalErrorException; +import org.apache.kylin.rest.service.DiagnosisService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; + +import com.google.common.collect.Lists; + +@Controller +@RequestMapping(value = "/diag") +public class DiagnosisController { + + private static final Logger logger = LoggerFactory.getLogger(DiagnosisController.class); + + @Autowired + private DiagnosisService dgService; + + /** + * Get bad query history + */ + @RequestMapping(value = "/{project}/sql", method = { RequestMethod.GET }) + @ResponseBody + public List<BadQueryEntry> getBadQuerySql(@PathVariable String project) { + + List<BadQueryEntry> badEntry = Lists.newArrayList(); + try { + BadQueryHistory badQueryHistory = dgService.getProjectBadQueryHistory(project); + badEntry.addAll(badQueryHistory.getEntries()); + } catch (IOException e) { + throw new InternalErrorException(e + " Caused by: " + e.getMessage(), e); + } + + return badEntry; + } + + /** + * Get diagnosis information for project + */ + @RequestMapping(value = "/project/{project}/download", method = { RequestMethod.GET }) + @ResponseBody + public void dumpProjectDiagnosisInfo(@PathVariable String project, final HttpServletRequest request, final HttpServletResponse response) { + String filePath; + try { + filePath = dgService.dumpProjectDiagnosisInfo(project); + } catch (IOException e) { + throw new InternalErrorException(e + " Caused by: " + e.getMessage(), e); + } + + setDownloadResponse(filePath, response); + } + + /** + * Get diagnosis information for job + */ + @RequestMapping(value = "/job/{jobId}/download", method = { RequestMethod.GET }) + @ResponseBody + public void dumpJobDiagnosisInfo(@PathVariable String jobId, final HttpServletRequest request, final HttpServletResponse response) { + String filePath; + try { + filePath = dgService.dumpJobDiagnosisInfo(jobId); + } catch (IOException e) { + throw new InternalErrorException(e + " Caused by: " + e.getMessage(), e); + } + + setDownloadResponse(filePath, response); + } + + private void setDownloadResponse(String downloadFile, final HttpServletResponse response) { + File file = new File(downloadFile); + try (InputStream fileInputStream = new FileInputStream(file); OutputStream output = response.getOutputStream();) { + response.reset(); + response.setContentType("application/octet-stream"); + response.setContentLength((int) (file.length())); + response.setHeader("Content-Disposition", "attachment; filename=\"" + file.getName() + "\""); + IOUtils.copyLarge(fileInputStream, output); + output.flush(); + } catch (IOException e) { + throw new InternalErrorException(e + " Caused by: " + e.getMessage(), e); + } + } +} http://git-wip-us.apache.org/repos/asf/kylin/blob/1a124e68/server-base/src/main/java/org/apache/kylin/rest/controller/ExternalFilterController.java ---------------------------------------------------------------------- diff --git a/server-base/src/main/java/org/apache/kylin/rest/controller/ExternalFilterController.java b/server-base/src/main/java/org/apache/kylin/rest/controller/ExternalFilterController.java new file mode 100644 index 0000000..7baa639 --- /dev/null +++ b/server-base/src/main/java/org/apache/kylin/rest/controller/ExternalFilterController.java @@ -0,0 +1,97 @@ +/* + * 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.kylin.rest.controller; + +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import org.apache.kylin.common.util.JsonUtil; +import org.apache.kylin.metadata.model.ExternalFilterDesc; +import org.apache.kylin.rest.request.ExternalFilterRequest; +import org.apache.kylin.rest.service.ExtFilterService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +import com.google.common.collect.Lists; + +/** + * @author jiazhong + */ +@Controller +@RequestMapping(value = "/extFilter") +public class ExternalFilterController extends BasicController { + private static final Logger logger = LoggerFactory.getLogger(ExternalFilterController.class); + + @Autowired + private ExtFilterService extFilterService; + + @RequestMapping(value = "/saveExtFilter", method = { RequestMethod.POST }) + @ResponseBody + public Map<String, String> saveExternalFilter(@RequestBody ExternalFilterRequest request) throws IOException { + Map<String, String> result = new HashMap(); + String filterProject = request.getProject(); + ExternalFilterDesc desc = JsonUtil.readValue(request.getExtFilter(), ExternalFilterDesc.class); + desc.setUuid(UUID.randomUUID().toString()); + extFilterService.saveExternalFilter(desc); + extFilterService.syncExtFilterToProject(new String[] { desc.getName() }, filterProject); + result.put("success", "true"); + return result; + } + + @RequestMapping(value = "/updateExtFilter", method = { RequestMethod.PUT }) + @ResponseBody + public Map<String, String> updateExternalFilter(@RequestBody ExternalFilterRequest request) throws IOException { + Map<String, String> result = new HashMap(); + ExternalFilterDesc desc = JsonUtil.readValue(request.getExtFilter(), ExternalFilterDesc.class); + extFilterService.updateExternalFilter(desc); + extFilterService.syncExtFilterToProject(new String[] { desc.getName() }, request.getProject()); + result.put("success", "true"); + return result; + } + + @RequestMapping(value = "/{filter}/{project}", method = { RequestMethod.DELETE }) + @ResponseBody + public Map<String, String> removeFilter(@PathVariable String filter, @PathVariable String project) throws IOException { + Map<String, String> result = new HashMap<String, String>(); + extFilterService.removeExtFilterFromProject(filter, project); + extFilterService.removeExternalFilter(filter); + result.put("success", "true"); + return result; + } + + @RequestMapping(value = "", method = { RequestMethod.GET }) + @ResponseBody + public List<ExternalFilterDesc> getExternalFilters(@RequestParam(value = "project", required = true) String project) throws IOException { + List<ExternalFilterDesc> filterDescs = Lists.newArrayList(); + filterDescs.addAll(extFilterService.getProjectManager().listExternalFilterDescs(project).values()); + return filterDescs; + } + +} http://git-wip-us.apache.org/repos/asf/kylin/blob/1a124e68/server-base/src/main/java/org/apache/kylin/rest/controller/JobController.java ---------------------------------------------------------------------- diff --git a/server-base/src/main/java/org/apache/kylin/rest/controller/JobController.java b/server-base/src/main/java/org/apache/kylin/rest/controller/JobController.java new file mode 100644 index 0000000..dce3847 --- /dev/null +++ b/server-base/src/main/java/org/apache/kylin/rest/controller/JobController.java @@ -0,0 +1,221 @@ +/* + * 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.kylin.rest.controller; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.TimeZone; + +import org.apache.kylin.common.KylinConfig; +import org.apache.kylin.common.util.ClassUtil; +import org.apache.kylin.job.JobInstance; +import org.apache.kylin.job.Scheduler; +import org.apache.kylin.job.SchedulerFactory; +import org.apache.kylin.job.constant.JobStatusEnum; +import org.apache.kylin.job.constant.JobTimeFilterEnum; +import org.apache.kylin.job.engine.JobEngineConfig; +import org.apache.kylin.job.exception.SchedulerException; +import org.apache.kylin.job.execution.AbstractExecutable; +import org.apache.kylin.job.lock.JobLock; +import org.apache.kylin.rest.exception.InternalErrorException; +import org.apache.kylin.rest.request.JobListRequest; +import org.apache.kylin.rest.service.JobService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; + +/** + * + */ +@Controller +@RequestMapping(value = "jobs") +public class JobController extends BasicController implements InitializingBean { + private static final Logger logger = LoggerFactory.getLogger(JobController.class); + + @Autowired + private JobService jobService; + + private JobLock jobLock; + + /* + * (non-Javadoc) + * + * @see + * org.springframework.beans.factory.InitializingBean#afterPropertiesSet() + */ + @SuppressWarnings("unchecked") + @Override + public void afterPropertiesSet() throws Exception { + + String timeZone = jobService.getConfig().getTimeZone(); + TimeZone tzone = TimeZone.getTimeZone(timeZone); + TimeZone.setDefault(tzone); + + final KylinConfig kylinConfig = KylinConfig.getInstanceFromEnv(); + final Scheduler<AbstractExecutable> scheduler = (Scheduler<AbstractExecutable>) SchedulerFactory.scheduler(kylinConfig.getSchedulerType()); + + jobLock = (JobLock) ClassUtil.newInstance(kylinConfig.getJobControllerLock()); + + new Thread(new Runnable() { + @Override + public void run() { + try { + scheduler.init(new JobEngineConfig(kylinConfig), jobLock); + while (!scheduler.hasStarted()) { + logger.error("scheduler has not been started"); + Thread.sleep(1000); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + }).start(); + + Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { + @Override + public void run() { + try { + scheduler.shutdown(); + } catch (SchedulerException e) { + logger.error("error occurred to shutdown scheduler", e); + } + } + })); + } + + /** + * get all cube jobs + * + * @return + * @throws IOException + */ + @RequestMapping(value = "", method = { RequestMethod.GET }) + @ResponseBody + public List<JobInstance> list(JobListRequest jobRequest) { + + List<JobInstance> jobInstanceList = Collections.emptyList(); + List<JobStatusEnum> statusList = new ArrayList<JobStatusEnum>(); + + if (null != jobRequest.getStatus()) { + for (int status : jobRequest.getStatus()) { + statusList.add(JobStatusEnum.getByCode(status)); + } + } + + JobTimeFilterEnum timeFilter = JobTimeFilterEnum.getByCode(jobRequest.getTimeFilter()); + + try { + jobInstanceList = jobService.listAllJobs(jobRequest.getCubeName(), jobRequest.getProjectName(), statusList, jobRequest.getLimit(), jobRequest.getOffset(), timeFilter); + } catch (Exception e) { + logger.error(e.getLocalizedMessage(), e); + throw new InternalErrorException(e); + } + return jobInstanceList; + } + + /** + * Get a cube job + * + * @return + * @throws IOException + */ + @RequestMapping(value = "/{jobId}", method = { RequestMethod.GET }) + @ResponseBody + public JobInstance get(@PathVariable String jobId) { + JobInstance jobInstance = null; + try { + jobInstance = jobService.getJobInstance(jobId); + } catch (Exception e) { + logger.error(e.getLocalizedMessage(), e); + throw new InternalErrorException(e); + } + + return jobInstance; + } + + /** + * Get a job step output + * + * @return + * @throws IOException + */ + @RequestMapping(value = "/{jobId}/steps/{stepId}/output", method = { RequestMethod.GET }) + @ResponseBody + public Map<String, String> getStepOutput(@PathVariable String jobId, @PathVariable String stepId) { + Map<String, String> result = new HashMap<String, String>(); + result.put("jobId", jobId); + result.put("stepId", String.valueOf(stepId)); + result.put("cmd_output", jobService.getExecutableManager().getOutput(stepId).getVerboseMsg()); + return result; + } + + /** + * Resume a cube job + * + * @return + * @throws IOException + */ + @RequestMapping(value = "/{jobId}/resume", method = { RequestMethod.PUT }) + @ResponseBody + public JobInstance resume(@PathVariable String jobId) { + try { + final JobInstance jobInstance = jobService.getJobInstance(jobId); + jobService.resumeJob(jobInstance); + return jobService.getJobInstance(jobId); + } catch (Exception e) { + logger.error(e.getLocalizedMessage(), e); + throw new InternalErrorException(e); + } + } + + /** + * Cancel a job + * + * @return + * @throws IOException + */ + @RequestMapping(value = "/{jobId}/cancel", method = { RequestMethod.PUT }) + @ResponseBody + public JobInstance cancel(@PathVariable String jobId) { + + try { + final JobInstance jobInstance = jobService.getJobInstance(jobId); + return jobService.cancelJob(jobInstance); + } catch (Exception e) { + logger.error(e.getLocalizedMessage(), e); + throw new InternalErrorException(e); + } + + } + + public void setJobService(JobService jobService) { + this.jobService = jobService; + } + +} http://git-wip-us.apache.org/repos/asf/kylin/blob/1a124e68/server-base/src/main/java/org/apache/kylin/rest/controller/ModelController.java ---------------------------------------------------------------------- diff --git a/server-base/src/main/java/org/apache/kylin/rest/controller/ModelController.java b/server-base/src/main/java/org/apache/kylin/rest/controller/ModelController.java new file mode 100644 index 0000000..5f6a91b --- /dev/null +++ b/server-base/src/main/java/org/apache/kylin/rest/controller/ModelController.java @@ -0,0 +1,236 @@ +/* + * 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.kylin.rest.controller; + +import java.io.IOException; +import java.util.Iterator; +import java.util.List; +import java.util.UUID; + +import org.apache.commons.lang.StringUtils; +import org.apache.kylin.common.KylinConfig; +import org.apache.kylin.common.util.JsonUtil; +import org.apache.kylin.metadata.MetadataManager; +import org.apache.kylin.metadata.model.DataModelDesc; +import org.apache.kylin.metadata.project.ProjectInstance; +import org.apache.kylin.rest.exception.BadRequestException; +import org.apache.kylin.rest.exception.ForbiddenException; +import org.apache.kylin.rest.exception.InternalErrorException; +import org.apache.kylin.rest.exception.NotFoundException; +import org.apache.kylin.rest.request.ModelRequest; +import org.apache.kylin.rest.service.ModelService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonMappingException; + +/** + * ModelController is defined as Restful API entrance for UI. + * + * @author jiazhong + */ +@Controller +@RequestMapping(value = "/models") +public class ModelController extends BasicController { + private static final Logger logger = LoggerFactory.getLogger(ModelController.class); + + @Autowired + private ModelService modelService; + + @RequestMapping(value = "", method = { RequestMethod.GET }) + @ResponseBody + public List<DataModelDesc> getModels(@RequestParam(value = "modelName", required = false) String modelName, @RequestParam(value = "projectName", required = false) String projectName, @RequestParam(value = "limit", required = false) Integer limit, @RequestParam(value = "offset", required = false) Integer offset) { + try { + return modelService.getModels(modelName, projectName, limit, offset); + } catch (IOException e) { + logger.error("Failed to deal with the request:" + e.getLocalizedMessage(), e); + throw new InternalErrorException("Failed to deal with the request: " + e.getLocalizedMessage()); + } + } + + /** + * + * create model + * @throws java.io.IOException + */ + @RequestMapping(value = "", method = { RequestMethod.POST }) + @ResponseBody + public ModelRequest saveModelDesc(@RequestBody ModelRequest modelRequest) { + //Update Model + DataModelDesc modelDesc = deserializeDataModelDesc(modelRequest); + if (modelDesc == null || StringUtils.isEmpty(modelDesc.getName())) { + return modelRequest; + } + + if (StringUtils.isEmpty(modelDesc.getName())) { + logger.info("Model name should not be empty."); + throw new BadRequestException("Model name should not be empty."); + } + + try { + modelDesc.setUuid(UUID.randomUUID().toString()); + String projectName = (null == modelRequest.getProject()) ? ProjectInstance.DEFAULT_PROJECT_NAME : modelRequest.getProject(); + + modelService.createModelDesc(projectName, modelDesc); + } catch (IOException e) { + // TODO Auto-generated catch block + logger.error("Failed to deal with the request:" + e.getLocalizedMessage(), e); + throw new InternalErrorException("Failed to deal with the request: " + e.getLocalizedMessage()); + } + + modelRequest.setUuid(modelDesc.getUuid()); + modelRequest.setSuccessful(true); + return modelRequest; + } + + @RequestMapping(value = "", method = { RequestMethod.PUT }) + @ResponseBody + public ModelRequest updateModelDesc(@RequestBody ModelRequest modelRequest) throws JsonProcessingException { + DataModelDesc modelDesc = deserializeDataModelDesc(modelRequest); + if (modelDesc == null) { + return modelRequest; + } + try { + modelDesc = modelService.updateModelAndDesc(modelDesc); + } catch (AccessDeniedException accessDeniedException) { + throw new ForbiddenException("You don't have right to update this model."); + } catch (Exception e) { + logger.error("Failed to deal with the request:" + e.getLocalizedMessage(), e); + throw new InternalErrorException("Failed to deal with the request: " + e.getLocalizedMessage()); + } + + if (modelDesc.getError().isEmpty()) { + modelRequest.setSuccessful(true); + } else { + logger.warn("Model " + modelDesc.getName() + " fail to update because " + modelDesc.getError()); + updateRequest(modelRequest, false, omitMessage(modelDesc.getError())); + } + String descData = JsonUtil.writeValueAsIndentString(modelDesc); + modelRequest.setModelDescData(descData); + return modelRequest; + } + + @RequestMapping(value = "/{modelName}", method = { RequestMethod.DELETE }) + @ResponseBody + public void deleteModel(@PathVariable String modelName) { + DataModelDesc desc = modelService.getMetadataManager().getDataModelDesc(modelName); + if (null == desc) { + throw new NotFoundException("Data Model with name " + modelName + " not found.."); + } + try { + modelService.dropModel(desc); + } catch (Exception e) { + logger.error(e.getLocalizedMessage(), e); + throw new InternalErrorException("Failed to delete model. " + " Caused by: " + e.getMessage(), e); + } + } + + @RequestMapping(value = "/{modelName}/clone", method = { RequestMethod.PUT }) + @ResponseBody + public ModelRequest cloneModel(@PathVariable String modelName, @RequestBody ModelRequest modelRequest) { + String project = modelRequest.getProject(); + MetadataManager metaManager = MetadataManager.getInstance(KylinConfig.getInstanceFromEnv()); + DataModelDesc modelDesc = metaManager.getDataModelDesc(modelName); + String newModelName = modelRequest.getModelName(); + + if (StringUtils.isEmpty(project)) { + logger.info("Project name should not be empty."); + throw new BadRequestException("Project name should not be empty."); + } + + if (modelDesc == null || StringUtils.isEmpty(modelName)) { + logger.info("Model does not exist."); + throw new BadRequestException("Model does not exist."); + } + + if (StringUtils.isEmpty(newModelName)) { + logger.info("New model name is empty."); + throw new BadRequestException("New model name is empty."); + } + + DataModelDesc newModelDesc = DataModelDesc.getCopyOf(modelDesc); + newModelDesc.setName(newModelName); + try { + newModelDesc = modelService.createModelDesc(project, newModelDesc); + + //reload avoid shallow + metaManager.reloadDataModelDesc(newModelName); + } catch (IOException e) { + throw new InternalErrorException("failed to clone DataModelDesc", e); + } + + modelRequest.setUuid(newModelDesc.getUuid()); + modelRequest.setSuccessful(true); + return modelRequest; + } + + private DataModelDesc deserializeDataModelDesc(ModelRequest modelRequest) { + DataModelDesc desc = null; + try { + logger.debug("Saving MODEL " + modelRequest.getModelDescData()); + desc = JsonUtil.readValue(modelRequest.getModelDescData(), DataModelDesc.class); + } catch (JsonParseException e) { + logger.error("The data model definition is not valid.", e); + updateRequest(modelRequest, false, e.getMessage()); + } catch (JsonMappingException e) { + logger.error("The data model definition is not valid.", e); + updateRequest(modelRequest, false, e.getMessage()); + } catch (IOException e) { + logger.error("Failed to deal with the request.", e); + throw new InternalErrorException("Failed to deal with the request:" + e.getMessage(), e); + } + return desc; + } + + private void updateRequest(ModelRequest request, boolean success, String message) { + request.setModelDescData(""); + request.setSuccessful(success); + request.setMessage(message); + } + + public void setModelService(ModelService modelService) { + this.modelService = modelService; + } + + /** + * @param errors + * @return + */ + private String omitMessage(List<String> errors) { + StringBuffer buffer = new StringBuffer(); + for (Iterator<String> iterator = errors.iterator(); iterator.hasNext();) { + String string = (String) iterator.next(); + buffer.append(string); + buffer.append("\n"); + } + return buffer.toString(); + } + +} http://git-wip-us.apache.org/repos/asf/kylin/blob/1a124e68/server-base/src/main/java/org/apache/kylin/rest/controller/ModelDescController.java ---------------------------------------------------------------------- diff --git a/server-base/src/main/java/org/apache/kylin/rest/controller/ModelDescController.java b/server-base/src/main/java/org/apache/kylin/rest/controller/ModelDescController.java new file mode 100644 index 0000000..4171afd --- /dev/null +++ b/server-base/src/main/java/org/apache/kylin/rest/controller/ModelDescController.java @@ -0,0 +1,57 @@ +/* + * 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.kylin.rest.controller; + +import java.io.IOException; + +import org.apache.kylin.common.KylinConfig; +import org.apache.kylin.metadata.MetadataManager; +import org.apache.kylin.metadata.model.DataModelDesc; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; + +/** + * @author jiazhong + * + */ +@Controller +@RequestMapping(value = "/model") +public class ModelDescController extends BasicController { + + /** + * Get detail information of the "Model ID" + * + * @param modelDescName + * Model ID + * @return + * @throws IOException + */ + @RequestMapping(value = "/{model_name}", method = { RequestMethod.GET }) + @ResponseBody + public DataModelDesc getModel(@PathVariable String model_name) { + MetadataManager metaManager = MetadataManager.getInstance(KylinConfig.getInstanceFromEnv()); + DataModelDesc modeDesc = metaManager.getDataModelDesc(model_name); + return modeDesc; + + } + +} http://git-wip-us.apache.org/repos/asf/kylin/blob/1a124e68/server-base/src/main/java/org/apache/kylin/rest/controller/ProjectController.java ---------------------------------------------------------------------- diff --git a/server-base/src/main/java/org/apache/kylin/rest/controller/ProjectController.java b/server-base/src/main/java/org/apache/kylin/rest/controller/ProjectController.java new file mode 100644 index 0000000..f829fff --- /dev/null +++ b/server-base/src/main/java/org/apache/kylin/rest/controller/ProjectController.java @@ -0,0 +1,257 @@ +/* + * 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.kylin.rest.controller; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang.StringUtils; +import org.apache.kylin.common.persistence.AclEntity; +import org.apache.kylin.cube.CubeInstance; +import org.apache.kylin.metadata.project.ProjectInstance; +import org.apache.kylin.rest.constant.Constant; +import org.apache.kylin.rest.exception.InternalErrorException; +import org.apache.kylin.rest.request.CreateProjectRequest; +import org.apache.kylin.rest.request.UpdateProjectRequest; +import org.apache.kylin.rest.service.AccessService; +import org.apache.kylin.rest.service.CubeService; +import org.apache.kylin.rest.service.ProjectService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.acls.domain.GrantedAuthoritySid; +import org.springframework.security.acls.domain.PrincipalSid; +import org.springframework.security.acls.model.AccessControlEntry; +import org.springframework.security.acls.model.Acl; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +/** + * @author xduo + */ +@Controller +@RequestMapping(value = "/projects") +public class ProjectController extends BasicController { + private static final Logger logger = LoggerFactory.getLogger(ProjectController.class); + + @Autowired + private ProjectService projectService; + @Autowired + private AccessService accessService; + @Autowired + private CubeService cubeService; + + /** + * Get available project list + * + * @return Table metadata array + * @throws IOException + */ + @RequestMapping(value = "", method = { RequestMethod.GET }) + @ResponseBody + public List<ProjectInstance> getProjects(@RequestParam(value = "limit", required = false) Integer limit, @RequestParam(value = "offset", required = false) Integer offset) { + return projectService.listAllProjects(limit, offset); + } + + @RequestMapping(value = "/readable", method = { RequestMethod.GET }) + @ResponseBody + public List<ProjectInstance> getReadableProjects(@RequestParam(value = "limit", required = false) Integer limit, @RequestParam(value = "offset", required = false) Integer offset) { + List<ProjectInstance> readableProjects = new ArrayList<ProjectInstance>(); + //list all projects first + List<ProjectInstance> projectInstances = projectService.listAllProjects(limit, offset); + + //get user infomation + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + UserDetails userDetails = null; + if (authentication == null) { + logger.debug("authentication is null."); + throw new InternalErrorException("Can not find authentication infomation."); + } + if (authentication.getPrincipal() instanceof UserDetails) { + logger.debug("authentication.getPrincipal() is " + authentication.getPrincipal()); + userDetails = (UserDetails) authentication.getPrincipal(); + } + if (authentication.getDetails() instanceof UserDetails) { + logger.debug("authentication.getDetails() is " + authentication.getDetails()); + userDetails = (UserDetails) authentication.getDetails(); + } + + //check if ROLE_ADMIN return all,also get user role list + List<String> userAuthority = new ArrayList<>(); + for (GrantedAuthority auth : authentication.getAuthorities()) { + userAuthority.add(auth.getAuthority()); + if (auth.getAuthority().equals(Constant.ROLE_ADMIN)) + return projectInstances; + } + String userName = userDetails.getUsername(); + for (ProjectInstance projectInstance : projectInstances) { + if (projectInstance == null) { + continue; + } + + boolean hasProjectPermission = false; + AclEntity ae = accessService.getAclEntity("ProjectInstance", projectInstance.getId()); + Acl projectAcl = accessService.getAcl(ae); + //project no Acl info will be skipped + if (projectAcl != null) { + + //project owner has permission + if (((PrincipalSid) projectAcl.getOwner()).getPrincipal().equals(userName)) { + readableProjects.add(projectInstance); + continue; + } + + //check project permission and role + for (AccessControlEntry ace : projectAcl.getEntries()) { + if (ace.getSid() instanceof PrincipalSid && ((PrincipalSid) ace.getSid()).getPrincipal().equals(userName)) { + hasProjectPermission = true; + readableProjects.add(projectInstance); + break; + + } else if (ace.getSid() instanceof GrantedAuthoritySid) { + String projectAuthority = ((GrantedAuthoritySid) ace.getSid()).getGrantedAuthority(); + if (userAuthority.contains(projectAuthority)) { + hasProjectPermission = true; + readableProjects.add(projectInstance); + break; + } + + } + + } + } + + if (!hasProjectPermission) { + List<CubeInstance> cubeInstances = cubeService.listAllCubes(projectInstance.getName()); + + for (CubeInstance cubeInstance : cubeInstances) { + if (cubeInstance == null) { + continue; + } + boolean hasCubePermission = false; + AclEntity cubeAe = accessService.getAclEntity("CubeInstance", cubeInstance.getId()); + Acl cubeAcl = accessService.getAcl(cubeAe); + //cube no Acl info will not be used to filter project + if (cubeAcl != null) { + //cube owner will have permission to read project + if (((PrincipalSid) cubeAcl.getOwner()).getPrincipal().equals(userName)) { + hasProjectPermission = true; + break; + } + for (AccessControlEntry cubeAce : cubeAcl.getEntries()) { + + if (cubeAce.getSid() instanceof PrincipalSid && ((PrincipalSid) cubeAce.getSid()).getPrincipal().equals(userName)) { + hasCubePermission = true; + break; + } else if (cubeAce.getSid() instanceof GrantedAuthoritySid) { + String cubeAuthority = ((GrantedAuthoritySid) cubeAce.getSid()).getGrantedAuthority(); + if (userAuthority.contains(cubeAuthority)) { + hasCubePermission = true; + break; + } + + } + } + } + if (hasCubePermission) { + hasProjectPermission = true; + break; + } + } + if (hasProjectPermission) { + readableProjects.add(projectInstance); + } + } + + } + return readableProjects; + } + + @RequestMapping(value = "", method = { RequestMethod.POST }) + @ResponseBody + public ProjectInstance saveProject(@RequestBody CreateProjectRequest projectRequest) { + if (StringUtils.isEmpty(projectRequest.getName())) { + throw new InternalErrorException("A project name must be given to create a project"); + } + + ProjectInstance createdProj = null; + try { + createdProj = projectService.createProject(projectRequest); + } catch (Exception e) { + logger.error("Failed to deal with the request.", e); + throw new InternalErrorException(e.getLocalizedMessage()); + } + + return createdProj; + } + + @RequestMapping(value = "", method = { RequestMethod.PUT }) + @ResponseBody + public ProjectInstance updateProject(@RequestBody UpdateProjectRequest projectRequest) { + if (StringUtils.isEmpty(projectRequest.getFormerProjectName())) { + throw new InternalErrorException("A project name must be given to update a project"); + } + + ProjectInstance updatedProj = null; + try { + ProjectInstance currentProject = projectService.getProjectManager().getProject(projectRequest.getFormerProjectName()); + updatedProj = projectService.updateProject(projectRequest, currentProject); + } catch (Exception e) { + logger.error("Failed to deal with the request.", e); + throw new InternalErrorException(e.getLocalizedMessage()); + } + + return updatedProj; + } + + @RequestMapping(value = "/{projectName}", method = { RequestMethod.DELETE }) + @ResponseBody + public void deleteProject(@PathVariable String projectName) { + try { + + ProjectInstance project = projectService.getProjectManager().getProject(projectName); + projectService.deleteProject(projectName, project); + } catch (Exception e) { + logger.error(e.getLocalizedMessage(), e); + throw new InternalErrorException("Failed to delete project. " + " Caused by: " + e.getMessage(), e); + } + } + + public void setProjectService(ProjectService projectService) { + this.projectService = projectService; + } + + public void setAccessService(AccessService accessService) { + this.accessService = accessService; + } + + public void setCubeService(CubeService cubeService) { + this.cubeService = cubeService; + } +} http://git-wip-us.apache.org/repos/asf/kylin/blob/1a124e68/server-base/src/main/java/org/apache/kylin/rest/controller/QueryController.java ---------------------------------------------------------------------- diff --git a/server-base/src/main/java/org/apache/kylin/rest/controller/QueryController.java b/server-base/src/main/java/org/apache/kylin/rest/controller/QueryController.java new file mode 100644 index 0000000..93b71ad --- /dev/null +++ b/server-base/src/main/java/org/apache/kylin/rest/controller/QueryController.java @@ -0,0 +1,270 @@ +/* + * 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.kylin.rest.controller; + +import java.io.IOException; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +import javax.annotation.PostConstruct; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.io.IOUtils; +import org.apache.kylin.common.KylinConfig; +import org.apache.kylin.common.debug.BackdoorToggles; +import org.apache.kylin.cube.CubeInstance; +import org.apache.kylin.rest.constant.Constant; +import org.apache.kylin.rest.exception.InternalErrorException; +import org.apache.kylin.rest.model.Query; +import org.apache.kylin.rest.model.SelectedColumnMeta; +import org.apache.kylin.rest.model.TableMeta; +import org.apache.kylin.rest.request.MetaRequest; +import org.apache.kylin.rest.request.PrepareSqlRequest; +import org.apache.kylin.rest.request.SQLRequest; +import org.apache.kylin.rest.request.SaveSqlRequest; +import org.apache.kylin.rest.response.SQLResponse; +import org.apache.kylin.rest.service.QueryService; +import org.apache.kylin.rest.util.QueryUtil; +import org.apache.kylin.storage.exception.ScanOutOfLimitException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; +import org.supercsv.io.CsvListWriter; +import org.supercsv.io.ICsvListWriter; +import org.supercsv.prefs.CsvPreference; + +import com.google.common.base.Preconditions; + +import net.sf.ehcache.Cache; +import net.sf.ehcache.CacheManager; +import net.sf.ehcache.Element; + +/** + * Handle query requests. + * + * @author xduo + */ +@Controller +public class QueryController extends BasicController { + + private static final Logger logger = LoggerFactory.getLogger(QueryController.class); + + public static final String SUCCESS_QUERY_CACHE = "StorageCache"; + public static final String EXCEPTION_QUERY_CACHE = "ExceptionQueryCache"; + + @Autowired + private QueryService queryService; + + @Autowired + private CacheManager cacheManager; + + @PostConstruct + public void init() throws IOException { + Preconditions.checkNotNull(cacheManager, "cacheManager is not injected yet"); + } + + @RequestMapping(value = "/query", method = RequestMethod.POST) + @ResponseBody + public SQLResponse query(@RequestBody SQLRequest sqlRequest) { + return doQueryWithCache(sqlRequest); + } + + // TODO should be just "prepare" a statement, get back expected ResultSetMetaData + @RequestMapping(value = "/query/prestate", method = RequestMethod.POST, produces = "application/json") + @ResponseBody + public SQLResponse prepareQuery(@RequestBody PrepareSqlRequest sqlRequest) { + return doQueryWithCache(sqlRequest); + } + + @RequestMapping(value = "/saved_queries", method = RequestMethod.POST) + @ResponseBody + public void saveQuery(@RequestBody SaveSqlRequest sqlRequest) throws IOException { + String creator = SecurityContextHolder.getContext().getAuthentication().getName(); + Query newQuery = new Query(sqlRequest.getName(), sqlRequest.getProject(), sqlRequest.getSql(), sqlRequest.getDescription()); + + queryService.saveQuery(creator, newQuery); + } + + @RequestMapping(value = "/saved_queries/{id}", method = RequestMethod.DELETE) + @ResponseBody + public void removeQuery(@PathVariable String id) throws IOException { + String creator = SecurityContextHolder.getContext().getAuthentication().getName(); + queryService.removeQuery(creator, id); + } + + @RequestMapping(value = "/saved_queries", method = RequestMethod.GET) + @ResponseBody + public List<Query> getQueries() throws IOException { + String creator = SecurityContextHolder.getContext().getAuthentication().getName(); + return queryService.getQueries(creator); + } + + @RequestMapping(value = "/query/format/{format}", method = RequestMethod.GET) + @ResponseBody + public void downloadQueryResult(@PathVariable String format, SQLRequest sqlRequest, HttpServletResponse response) { + SQLResponse result = doQueryWithCache(sqlRequest); + response.setContentType("text/" + format + ";charset=utf-8"); + response.setHeader("Content-Disposition", "attachment; filename=\"result." + format + "\""); + ICsvListWriter csvWriter = null; + + try { + csvWriter = new CsvListWriter(response.getWriter(), CsvPreference.STANDARD_PREFERENCE); + + List<String> headerList = new ArrayList<String>(); + + for (SelectedColumnMeta column : result.getColumnMetas()) { + headerList.add(column.getName()); + } + + String[] headers = new String[headerList.size()]; + csvWriter.writeHeader(headerList.toArray(headers)); + + for (List<String> row : result.getResults()) { + csvWriter.write(row); + } + } catch (IOException e) { + logger.error("", e); + } finally { + IOUtils.closeQuietly(csvWriter); + } + } + + @RequestMapping(value = "/tables_and_columns", method = RequestMethod.GET) + @ResponseBody + public List<TableMeta> getMetadata(MetaRequest metaRequest) { + try { + return queryService.getMetadata(metaRequest.getProject()); + } catch (SQLException e) { + logger.error(e.getLocalizedMessage(), e); + throw new InternalErrorException(e.getLocalizedMessage(), e); + } + } + + private SQLResponse doQueryWithCache(SQLRequest sqlRequest) { + try { + BackdoorToggles.setToggles(sqlRequest.getBackdoorToggles()); + + String sql = sqlRequest.getSql(); + String project = sqlRequest.getProject(); + logger.info("Using project: " + project); + logger.info("The original query: " + sql); + + String serverMode = KylinConfig.getInstanceFromEnv().getServerMode(); + if (!(Constant.SERVER_MODE_QUERY.equals(serverMode.toLowerCase()) || Constant.SERVER_MODE_ALL.equals(serverMode.toLowerCase()))) { + throw new InternalErrorException("Query is not allowed in " + serverMode + " mode."); + } + + if (!sql.toLowerCase().contains("select")) { + logger.debug("Directly return exception as not supported"); + throw new InternalErrorException("Not Supported SQL."); + } + + long startTime = System.currentTimeMillis(); + + SQLResponse sqlResponse = searchQueryInCache(sqlRequest); + try { + if (null == sqlResponse) { + sqlResponse = queryService.query(sqlRequest); + + long durationThreshold = KylinConfig.getInstanceFromEnv().getQueryDurationCacheThreshold(); + long scancountThreshold = KylinConfig.getInstanceFromEnv().getQueryScanCountCacheThreshold(); + sqlResponse.setDuration(System.currentTimeMillis() - startTime); + logger.info("Stats of SQL response: isException: {}, duration: {}, total scan count {}", // + new String[] { String.valueOf(sqlResponse.getIsException()), String.valueOf(sqlResponse.getDuration()), String.valueOf(sqlResponse.getTotalScanCount()) }); + if (!sqlResponse.getIsException() && (sqlResponse.getDuration() > durationThreshold || sqlResponse.getTotalScanCount() > scancountThreshold)) { + cacheManager.getCache(SUCCESS_QUERY_CACHE).put(new Element(sqlRequest, sqlResponse)); + } + } else { + sqlResponse.setDuration(System.currentTimeMillis() - startTime); + } + + checkQueryAuth(sqlResponse); + + } catch (Throwable e) { // calcite may throw AssertError + logger.error("Exception when execute sql", e); + String errMsg = QueryUtil.makeErrorMsgUserFriendly(e); + + sqlResponse = new SQLResponse(null, null, 0, true, errMsg); + + // for exception queries, only cache ScanOutOfLimitException + if (e instanceof ScanOutOfLimitException) { + Cache exceptionCache = cacheManager.getCache(EXCEPTION_QUERY_CACHE); + exceptionCache.put(new Element(sqlRequest, sqlResponse)); + } + } + + queryService.logQuery(sqlRequest, sqlResponse); + + if (sqlResponse.getIsException()) + throw new InternalErrorException(sqlResponse.getExceptionMessage()); + + return sqlResponse; + + } finally { + BackdoorToggles.cleanToggles(); + } + } + + private SQLResponse searchQueryInCache(SQLRequest sqlRequest) { + SQLResponse response = null; + Cache exceptionCache = cacheManager.getCache(EXCEPTION_QUERY_CACHE); + Cache successCache = cacheManager.getCache(SUCCESS_QUERY_CACHE); + + if (KylinConfig.getInstanceFromEnv().isQueryCacheEnabled() && !BackdoorToggles.getDisableCache()) { + if (exceptionCache.get(sqlRequest) != null) { + logger.info("The sqlResponse is found in EXCEPTION_QUERY_CACHE"); + Element element = exceptionCache.get(sqlRequest); + response = (SQLResponse) element.getObjectValue(); + response.setHitExceptionCache(true); + } else if (successCache.get(sqlRequest) != null) { + logger.info("The sqlResponse is found in SUCCESS_QUERY_CACHE"); + Element element = successCache.get(sqlRequest); + response = (SQLResponse) element.getObjectValue(); + response.setStorageCacheUsed(true); + } + } + + return response; + } + + private void checkQueryAuth(SQLResponse sqlResponse) throws AccessDeniedException { + if (!sqlResponse.getIsException() && KylinConfig.getInstanceFromEnv().isQuerySecureEnabled()) { + CubeInstance cubeInstance = this.queryService.getCubeManager().getCube(sqlResponse.getCube()); + queryService.checkAuthorization(cubeInstance); + } + } + + public void setQueryService(QueryService queryService) { + this.queryService = queryService; + } + + public void setCacheManager(CacheManager cacheManager) { + this.cacheManager = cacheManager; + } + +}
