kylin-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From kangkai...@apache.org
Subject [22/50] [abbrv] kylin git commit: KYLIN-2632 Refactor REST API for better convention
Date Wed, 24 May 2017 10:42:53 GMT
http://git-wip-us.apache.org/repos/asf/kylin/blob/73a78dbe/server-base/src/main/java/org/apache/kylin/rest/service/CubeServiceV2.java
----------------------------------------------------------------------
diff --git a/server-base/src/main/java/org/apache/kylin/rest/service/CubeServiceV2.java b/server-base/src/main/java/org/apache/kylin/rest/service/CubeServiceV2.java
new file mode 100644
index 0000000..2fae789
--- /dev/null
+++ b/server-base/src/main/java/org/apache/kylin/rest/service/CubeServiceV2.java
@@ -0,0 +1,497 @@
+/*
+ * 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.service;
+
+import static org.apache.kylin.cube.model.CubeDesc.STATUS_DRAFT;
+import static org.apache.kylin.rest.controller2.CubeControllerV2.VALID_CUBENAME;
+
+import java.io.IOException;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.UUID;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.kylin.cube.CubeInstance;
+import org.apache.kylin.cube.CubeManager;
+import org.apache.kylin.cube.CubeSegment;
+import org.apache.kylin.cube.CubeUpdate;
+import org.apache.kylin.cube.cuboid.CuboidCLI;
+import org.apache.kylin.cube.model.CubeDesc;
+import org.apache.kylin.engine.mr.CubingJob;
+import org.apache.kylin.job.exception.JobException;
+import org.apache.kylin.job.execution.ExecutableState;
+import org.apache.kylin.metadata.model.DataModelDesc;
+import org.apache.kylin.metadata.model.SegmentStatusEnum;
+import org.apache.kylin.metadata.project.ProjectInstance;
+import org.apache.kylin.metadata.project.ProjectManager;
+import org.apache.kylin.metadata.realization.RealizationStatusEnum;
+import org.apache.kylin.metadata.realization.RealizationType;
+import org.apache.kylin.rest.constant.Constant;
+import org.apache.kylin.rest.exception.BadRequestException;
+import org.apache.kylin.rest.exception.ForbiddenException;
+import org.apache.kylin.rest.msg.Message;
+import org.apache.kylin.rest.msg.MsgPicker;
+import org.apache.kylin.rest.security.AclPermission;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Component;
+
+/**
+ * Created by luwei on 17-4-17.
+ */
+
+@Component("cubeMgmtServiceV2")
+public class CubeServiceV2 extends CubeService {
+
+    private static final Logger logger = LoggerFactory.getLogger(CubeServiceV2.class);
+
+    @Autowired
+    @Qualifier("accessService")
+    private AccessService accessService;
+
+    @Autowired
+    @Qualifier("jobService")
+    private JobService jobService;
+
+    @Autowired
+    @Qualifier("modelMgmtServiceV2")
+    private ModelServiceV2 modelServiceV2;
+
+
+    @PreAuthorize(Constant.ACCESS_HAS_ROLE_ADMIN + " or hasPermission(#cube, 'ADMINISTRATION') or hasPermission(#cube, 'OPERATION')  or hasPermission(#cube, 'MANAGEMENT')")
+    public CubeInstance deleteSegment(CubeInstance cube, String segmentName) throws IOException {
+        Message msg = MsgPicker.getMsg();
+
+        if (!segmentName.equals(cube.getSegments().get(0).getName()) && !segmentName.equals(cube.getSegments().get(cube.getSegments().size() - 1).getName())) {
+            throw new BadRequestException(String.format(msg.getDELETE_NOT_FIRST_LAST_SEG(), segmentName));
+        }
+        CubeSegment toDelete = null;
+        for (CubeSegment seg : cube.getSegments()) {
+            if (seg.getName().equals(segmentName)) {
+                toDelete = seg;
+            }
+        }
+
+        if (toDelete == null) {
+            throw new BadRequestException(String.format(msg.getSEG_NOT_FOUND(), segmentName));
+        }
+
+        if (toDelete.getStatus() != SegmentStatusEnum.READY) {
+            throw new BadRequestException(String.format(msg.getDELETE_NOT_READY_SEG(), segmentName));
+        }
+
+        CubeUpdate update = new CubeUpdate(cube);
+        update.setToRemoveSegs(new CubeSegment[] { toDelete });
+        return CubeManager.getInstance(getConfig()).updateCube(update);
+    }
+
+    /**
+     * Update a cube status from ready to disabled.
+     *
+     * @return
+     * @throws IOException
+     */
+    @PreAuthorize(Constant.ACCESS_HAS_ROLE_ADMIN + " or hasPermission(#cube, 'ADMINISTRATION') or hasPermission(#cube, 'OPERATION') or hasPermission(#cube, 'MANAGEMENT')")
+    public CubeInstance disableCube(CubeInstance cube) throws IOException {
+        Message msg = MsgPicker.getMsg();
+
+        String cubeName = cube.getName();
+
+        RealizationStatusEnum ostatus = cube.getStatus();
+        if (null != ostatus && !RealizationStatusEnum.READY.equals(ostatus)) {
+            throw new BadRequestException(String.format(msg.getDISABLE_NOT_READY_CUBE(), cubeName, ostatus));
+        }
+
+        cube.setStatus(RealizationStatusEnum.DISABLED);
+
+        try {
+            CubeUpdate cubeBuilder = new CubeUpdate(cube);
+            cubeBuilder.setStatus(RealizationStatusEnum.DISABLED);
+            return getCubeManager().updateCube(cubeBuilder);
+        } catch (IOException e) {
+            cube.setStatus(ostatus);
+            throw e;
+        }
+    }
+
+    /**
+     * Stop all jobs belonging to this cube and clean out all segments
+     *
+     * @param cube
+     * @return
+     * @throws IOException
+     */
+    @PreAuthorize(Constant.ACCESS_HAS_ROLE_ADMIN + " or hasPermission(#cube, 'ADMINISTRATION') or hasPermission(#cube, 'OPERATION') or hasPermission(#cube, 'MANAGEMENT')")
+    public CubeInstance purgeCube(CubeInstance cube) throws IOException {
+        Message msg = MsgPicker.getMsg();
+
+        String cubeName = cube.getName();
+        RealizationStatusEnum ostatus = cube.getStatus();
+        if (null != ostatus && !RealizationStatusEnum.DISABLED.equals(ostatus)) {
+            throw new BadRequestException(String.format(msg.getPURGE_NOT_DISABLED_CUBE(), cubeName, ostatus));
+        }
+
+        this.releaseAllSegments(cube);
+        return cube;
+
+    }
+
+    /**
+     * purge the cube
+     *
+     * @throws IOException
+     * @throws JobException
+     */
+    protected void releaseAllSegments(CubeInstance cube) throws IOException {
+        releaseAllJobs(cube);
+
+        CubeUpdate update = new CubeUpdate(cube);
+        update.setToRemoveSegs(cube.getSegments().toArray(new CubeSegment[cube.getSegments().size()]));
+        CubeManager.getInstance(getConfig()).updateCube(update);
+    }
+
+    protected void releaseAllJobs(CubeInstance cube) {
+        final List<CubingJob> cubingJobs = jobService.listAllCubingJobs(cube.getName(), null);
+        for (CubingJob cubingJob : cubingJobs) {
+            final ExecutableState status = cubingJob.getStatus();
+            if (status != ExecutableState.SUCCEED && status != ExecutableState.DISCARDED) {
+                getExecutableManager().discardJob(cubingJob.getId());
+            }
+        }
+    }
+
+    @PreAuthorize(Constant.ACCESS_HAS_ROLE_ADMIN + " or " + Constant.ACCESS_HAS_ROLE_MODELER)
+    public CubeInstance createCubeAndDesc(String cubeName, String projectName, CubeDesc desc) throws IOException {
+        Message msg = MsgPicker.getMsg();
+
+        if (getCubeManager().getCube(cubeName) != null) {
+            throw new BadRequestException(String.format(msg.getCUBE_ALREADY_EXIST(), cubeName));
+        }
+
+        if (getCubeDescManager().getCubeDesc(desc.getName()) != null) {
+            throw new BadRequestException(String.format(msg.getCUBE_DESC_ALREADY_EXIST(), desc.getName()));
+        }
+
+        String owner = SecurityContextHolder.getContext().getAuthentication().getName();
+        CubeDesc createdDesc;
+        CubeInstance createdCube;
+
+        createdDesc = getCubeDescManager().createCubeDesc(desc);
+
+        if (!createdDesc.getError().isEmpty()) {
+            getCubeDescManager().removeCubeDesc(createdDesc);
+            throw new BadRequestException(String.format(msg.getBROKEN_CUBE_DESC(), cubeName));
+        }
+
+        if (desc.getStatus() == null) {
+            try {
+                int cuboidCount = CuboidCLI.simulateCuboidGeneration(createdDesc, false);
+                logger.info("New cube " + cubeName + " has " + cuboidCount + " cuboids");
+            } catch (Exception e) {
+                getCubeDescManager().removeCubeDesc(createdDesc);
+                throw e;
+            }
+        }
+
+        createdCube = getCubeManager().createCube(cubeName, projectName, createdDesc, owner);
+        accessService.init(createdCube, AclPermission.ADMINISTRATION);
+
+        ProjectInstance project = getProjectManager().getProject(projectName);
+        accessService.inherit(createdCube, project);
+
+        return createdCube;
+    }
+
+    /**
+     * Update a cube status from disable to ready.
+     *
+     * @return
+     * @throws IOException
+     */
+    @PreAuthorize(Constant.ACCESS_HAS_ROLE_ADMIN + " or hasPermission(#cube, 'ADMINISTRATION') or hasPermission(#cube, 'OPERATION')  or hasPermission(#cube, 'MANAGEMENT')")
+    public CubeInstance enableCube(CubeInstance cube) throws IOException {
+        Message msg = MsgPicker.getMsg();
+
+        String cubeName = cube.getName();
+
+        RealizationStatusEnum ostatus = cube.getStatus();
+        if (!cube.getStatus().equals(RealizationStatusEnum.DISABLED)) {
+            throw new BadRequestException(String.format(msg.getENABLE_NOT_DISABLED_CUBE(), cubeName, ostatus));
+        }
+
+        if (cube.getSegments(SegmentStatusEnum.READY).size() == 0) {
+            throw new BadRequestException(String.format(msg.getNO_READY_SEGMENT(), cubeName));
+        }
+
+        final List<CubingJob> cubingJobs = jobService.listAllCubingJobs(cube.getName(), null, EnumSet.of(ExecutableState.READY, ExecutableState.RUNNING));
+        if (!cubingJobs.isEmpty()) {
+            throw new BadRequestException(msg.getENABLE_WITH_RUNNING_JOB());
+        }
+        if (!cube.getDescriptor().checkSignature()) {
+            throw new BadRequestException(String.format(msg.getINCONSISTENT_CUBE_DESC_SIGNATURE(), cube.getDescriptor()));
+        }
+
+        try {
+            CubeUpdate cubeBuilder = new CubeUpdate(cube);
+            cubeBuilder.setStatus(RealizationStatusEnum.READY);
+            return getCubeManager().updateCube(cubeBuilder);
+        } catch (IOException e) {
+            cube.setStatus(ostatus);
+            throw e;
+        }
+    }
+
+    @PreAuthorize(Constant.ACCESS_HAS_ROLE_ADMIN + " or hasPermission(#cube, 'ADMINISTRATION') or hasPermission(#cube, 'MANAGEMENT')")
+    public void deleteCube(CubeInstance cube) throws IOException {
+        Message msg = MsgPicker.getMsg();
+
+        final List<CubingJob> cubingJobs = jobService.listAllCubingJobs(cube.getName(), null, EnumSet.of(ExecutableState.READY, ExecutableState.RUNNING, ExecutableState.ERROR));
+        if (!cubingJobs.isEmpty()) {
+            throw new BadRequestException(String.format(msg.getDISCARD_JOB_FIRST(), cube.getName()));
+        }
+
+        try {
+            this.releaseAllJobs(cube);
+        } catch (Exception e) {
+            logger.error("error when releasing all jobs", e);
+            //ignore the exception
+        }
+
+        int cubeNum = getCubeManager().getCubesByDesc(cube.getDescriptor().getName()).size();
+        getCubeManager().dropCube(cube.getName(), cubeNum == 1);//only delete cube desc when no other cube is using it
+        accessService.clean(cube, true);
+    }
+
+    @PreAuthorize(Constant.ACCESS_HAS_ROLE_ADMIN + " or hasPermission(#cube, 'ADMINISTRATION') or hasPermission(#cube, 'MANAGEMENT')")
+    public CubeDesc updateCubeAndDesc(CubeInstance cube, CubeDesc desc, String newProjectName, boolean forceUpdate) throws IOException {
+        Message msg = MsgPicker.getMsg();
+
+        final List<CubingJob> cubingJobs = jobService.listAllCubingJobs(cube.getName(), null, EnumSet.of(ExecutableState.READY, ExecutableState.RUNNING));
+        if (!cubingJobs.isEmpty()) {
+            throw new BadRequestException(String.format(msg.getDISCARD_JOB_FIRST(), cube.getName()));
+        }
+
+        //double check again
+        if (!forceUpdate && !cube.getDescriptor().consistentWith(desc)) {
+            throw new BadRequestException(String.format(msg.getINCONSISTENT_CUBE_DESC(), desc.getName()));
+        }
+
+        CubeDesc updatedCubeDesc = getCubeDescManager().updateCubeDesc(desc);
+        if (desc.getStatus() == null) {
+            int cuboidCount = CuboidCLI.simulateCuboidGeneration(updatedCubeDesc, false);
+            logger.info("Updated cube " + cube.getName() + " has " + cuboidCount + " cuboids");
+        }
+
+        ProjectManager projectManager = getProjectManager();
+        if (!isCubeInProject(newProjectName, cube)) {
+            String owner = SecurityContextHolder.getContext().getAuthentication().getName();
+            ProjectInstance newProject = projectManager.moveRealizationToProject(RealizationType.CUBE, cube.getName(), newProjectName, owner);
+            accessService.inherit(cube, newProject);
+        }
+
+        return updatedCubeDesc;
+    }
+
+    public void validateCubeDesc(CubeDesc desc, boolean isDraft) {
+        Message msg = MsgPicker.getMsg();
+
+        if (desc == null) {
+            throw new BadRequestException(msg.getINVALID_CUBE_DEFINITION());
+        }
+
+        String cubeName = desc.getName();
+        if (StringUtils.isEmpty(cubeName)) {
+            logger.info("Cube name should not be empty.");
+            throw new BadRequestException(msg.getEMPTY_CUBE_NAME());
+        }
+        if (!StringUtils.containsOnly(cubeName, VALID_CUBENAME)) {
+            logger.info("Invalid Cube name {}, only letters, numbers and underline supported.", cubeName);
+            throw new BadRequestException(String.format(msg.getINVALID_CUBE_NAME(), cubeName));
+        }
+
+        if (!isDraft) {
+            DataModelDesc modelDesc = modelServiceV2.getMetadataManager().getDataModelDesc(desc.getModelName());
+            if (modelDesc == null) {
+                throw new BadRequestException(String.format(msg.getMODEL_NOT_FOUND(), desc.getModelName()));
+            }
+
+            String modelDescStatus = modelDesc.getStatus();
+            if (modelDescStatus != null && modelDescStatus.equals(DataModelDesc.STATUS_DRAFT)) {
+                logger.info("Cannot use draft model.");
+                throw new BadRequestException(String.format(msg.getUSE_DRAFT_MODEL(), desc.getModelName()));
+            }
+        }
+    }
+
+    public boolean unifyCubeDesc(CubeDesc desc, boolean isDraft) throws IOException {
+        Message msg = MsgPicker.getMsg();
+
+        boolean createNew = false;
+        String cubeName = desc.getName();
+        String originName = null;   // for draft rename check
+        if (desc.getUuid() != null) {
+            originName = getNameByUuid(desc.getUuid());
+        }
+
+        if (!isDraft) { // save as official cube
+            if (desc.getStatus() != null && desc.getStatus().equals(STATUS_DRAFT)) {  // from draft
+                if (originName == null) {
+                    throw new BadRequestException(msg.getORIGIN_CUBE_NOT_FOUND());
+                }
+                originName = originName.substring(0, originName.lastIndexOf("_draft"));
+                if (!originName.equals(cubeName)) {     // if rename draft
+                    CubeDesc parentDesc = getCubeDescManager().getCubeDesc(originName);
+                    if (parentDesc == null) {   // only allow rename when official cube has not been saved
+                        createNew = true;
+                        deleteCubeByUuid(desc.getUuid());
+                        desc.setStatus(null);
+                        desc.setLastModified(0);
+                        desc.setUuid(UUID.randomUUID().toString());
+                    } else {
+                        throw new BadRequestException(msg.getCUBE_RENAME());
+                    }
+                } else {    // without rename draft
+                    desc.setStatus(null);
+                    CubeDesc parentDesc = getCubeDescManager().getCubeDesc(cubeName);
+                    if (parentDesc == null) {   // official cube doesn't exist, create new one
+                        createNew = true;
+                        desc.setLastModified(0);
+                        desc.setUuid(UUID.randomUUID().toString());
+                    } else {    // update existing
+                        desc.setLastModified(parentDesc.getLastModified());
+                        desc.setUuid(parentDesc.getUuid());
+                    }
+                }
+            } else {    // from official
+                if (originName == null) {   // official cube doesn't exist, create new one
+                    createNew = true;
+                    desc.setLastModified(0);
+                    desc.setUuid(UUID.randomUUID().toString());
+                } else {
+                    if (!originName.equals(cubeName)) {    // do not allow official cube rename
+                        throw new BadRequestException(msg.getCUBE_RENAME());
+                    }
+                }
+            }
+        } else {    // save as draft model
+            if (desc.getStatus() == null) {    // from official
+                cubeName += "_draft";
+                desc.setName(cubeName);
+                desc.setStatus(STATUS_DRAFT);
+                CubeDesc draftDesc = getCubeDescManager().getCubeDesc(cubeName);
+                if (draftDesc == null) {
+                    createNew = true;
+                    desc.setLastModified(0);
+                    desc.setUuid(UUID.randomUUID().toString());
+                } else if (draftDesc.getStatus() != null && draftDesc.getStatus().equals(STATUS_DRAFT)) {   // update existing
+                    desc.setLastModified(draftDesc.getLastModified());
+                    desc.setUuid(draftDesc.getUuid());
+                } else {    // already exist an official draft with name ends with '_draft'
+                    throw new BadRequestException(String.format(msg.getNON_DRAFT_CUBE_ALREADY_EXIST(), cubeName));
+                }
+            } else if (desc.getStatus().equals(STATUS_DRAFT)) {    // from draft
+                if (originName == null) {
+                    throw new BadRequestException(msg.getORIGIN_CUBE_NOT_FOUND());
+                }
+                originName = originName.substring(0, originName.lastIndexOf("_draft"));
+                if (!originName.equals(cubeName)) {    // if rename draft
+                    CubeDesc parentDesc = getCubeDescManager().getCubeDesc(originName);
+                    if (parentDesc == null) {   // only allow rename when official cube has not been saved
+                        createNew = true;
+                        deleteCubeByUuid(desc.getUuid());
+                        cubeName += "_draft";
+                        desc.setName(cubeName);
+                        desc.setStatus(STATUS_DRAFT);
+                        desc.setLastModified(0);
+                        desc.setUuid(UUID.randomUUID().toString());
+                    } else {
+                        throw new BadRequestException(msg.getMODEL_RENAME());
+                    }
+                } else {    // without rename draft
+                    cubeName += "_draft";
+                    desc.setName(cubeName);
+                }
+            }
+        }
+        return createNew;
+    }
+
+    public String getNameByUuid(String uuid) {
+        List<CubeInstance> cubes = getCubeManager().listAllCubes();
+        for (CubeInstance cube : cubes) {
+            if (cube.getDescriptor().getUuid().equals(uuid)) {
+                return cube.getName();
+            }
+        }
+        return null;
+    }
+
+    public void deleteCubeByUuid(String uuid) throws IOException {
+        List<CubeInstance> cubes = getCubeManager().listAllCubes();
+        for (CubeInstance cube : cubes) {
+            if (cube.getDescriptor().getUuid().equals(uuid)) {
+                deleteCube(cube);
+            }
+        }
+    }
+
+    public CubeDesc updateCubeToResourceStore(CubeDesc desc, String projectName, boolean createNew, boolean isDraft) throws IOException {
+        Message msg = MsgPicker.getMsg();
+
+        String cubeName = desc.getName();
+        if (createNew) {
+            createCubeAndDesc(cubeName, projectName, desc);
+        } else {
+            try {
+                CubeInstance cube = getCubeManager().getCube(desc.getName());
+
+                if (cube == null) {
+                    throw new BadRequestException(String.format(msg.getCUBE_NOT_FOUND(), desc.getName()));
+                }
+
+                if (cube.getSegments().size() != 0 && !cube.getDescriptor().consistentWith(desc)) {
+                    throw new BadRequestException(String.format(msg.getINCONSISTENT_CUBE_DESC(), desc.getName()));
+                }
+
+                desc = updateCubeAndDesc(cube, desc, projectName, true);
+            } catch (AccessDeniedException accessDeniedException) {
+                throw new ForbiddenException(msg.getUPDATE_CUBE_NO_RIGHT());
+            }
+
+            if (!desc.getError().isEmpty()) {
+                throw new BadRequestException(String.format(msg.getBROKEN_CUBE_DESC(), cubeName));
+            }
+        }
+
+        if (!isDraft) {
+            String draftName = desc.getName() + "_draft";
+            CubeInstance draftCube = getCubeManager().getCube(draftName);
+            if (null != draftCube && draftCube.getDescriptor().getStatus() != null && draftCube.getDescriptor().getStatus().equals(STATUS_DRAFT)) {
+                //drop draft Cube
+                deleteCube(draftCube);
+            }
+        }
+        return desc;
+    }
+}

http://git-wip-us.apache.org/repos/asf/kylin/blob/73a78dbe/server-base/src/main/java/org/apache/kylin/rest/service/DiagnosisService.java
----------------------------------------------------------------------
diff --git a/server-base/src/main/java/org/apache/kylin/rest/service/DiagnosisService.java b/server-base/src/main/java/org/apache/kylin/rest/service/DiagnosisService.java
index b473071..35b018c 100644
--- a/server-base/src/main/java/org/apache/kylin/rest/service/DiagnosisService.java
+++ b/server-base/src/main/java/org/apache/kylin/rest/service/DiagnosisService.java
@@ -28,6 +28,9 @@ import org.apache.kylin.common.util.CliCommandExecutor;
 import org.apache.kylin.common.util.Pair;
 import org.apache.kylin.metadata.badquery.BadQueryHistory;
 import org.apache.kylin.rest.constant.Constant;
+import org.apache.kylin.rest.exception.BadRequestException;
+import org.apache.kylin.rest.msg.Message;
+import org.apache.kylin.rest.msg.MsgPicker;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.security.access.prepost.PreAuthorize;
@@ -40,14 +43,16 @@ public class DiagnosisService extends BasicService {
 
     private static final Logger logger = LoggerFactory.getLogger(DiagnosisService.class);
 
-    private File getDumpDir() {
+    protected File getDumpDir() {
         return Files.createTempDir();
     }
 
     private String getDiagnosisPackageName(File destDir) {
+        Message msg = MsgPicker.getMsg();
+
         File[] files = destDir.listFiles();
         if (files == null) {
-            throw new RuntimeException("Diagnosis package is not available in directory: " + destDir.getAbsolutePath());
+            throw new BadRequestException(String.format(msg.getDIAG_PACKAGE_NOT_AVAILABLE(), destDir.getAbsolutePath()));
         }
         for (File subDir : files) {
             if (subDir.isDirectory()) {
@@ -58,7 +63,7 @@ public class DiagnosisService extends BasicService {
                 }
             }
         }
-        throw new RuntimeException("Diagnosis package not found in directory: " + destDir.getAbsolutePath());
+        throw new BadRequestException(String.format(msg.getDIAG_PACKAGE_NOT_FOUND(), destDir.getAbsolutePath()));
     }
 
     @PreAuthorize(Constant.ACCESS_HAS_ROLE_ADMIN)
@@ -83,13 +88,15 @@ public class DiagnosisService extends BasicService {
     }
 
     private void runDiagnosisCLI(String[] args) throws IOException {
+        Message msg = MsgPicker.getMsg();
+
         File cwd = new File("");
         logger.debug("Current path: " + cwd.getAbsolutePath());
 
         logger.debug("DiagnosisInfoCLI args: " + Arrays.toString(args));
         File script = new File(KylinConfig.getKylinHome() + File.separator + "bin", "diag.sh");
         if (!script.exists()) {
-            throw new RuntimeException("diag.sh not found at " + script.getAbsolutePath());
+            throw new BadRequestException(String.format(msg.getDIAG_NOT_FOUND(), script.getAbsolutePath()));
         }
 
         String diagCmd = script.getAbsolutePath() + " " + StringUtils.join(args, " ");
@@ -97,7 +104,8 @@ public class DiagnosisService extends BasicService {
         Pair<Integer, String> cmdOutput = executor.execute(diagCmd);
 
         if (cmdOutput.getKey() != 0) {
-            throw new RuntimeException("Failed to generate diagnosis package.");
+            throw new BadRequestException(msg.getGENERATE_DIAG_PACKAGE_FAIL());
         }
     }
+
 }

http://git-wip-us.apache.org/repos/asf/kylin/blob/73a78dbe/server-base/src/main/java/org/apache/kylin/rest/service/EncodingService.java
----------------------------------------------------------------------
diff --git a/server-base/src/main/java/org/apache/kylin/rest/service/EncodingService.java b/server-base/src/main/java/org/apache/kylin/rest/service/EncodingService.java
index 60f9974..f3742de 100644
--- a/server-base/src/main/java/org/apache/kylin/rest/service/EncodingService.java
+++ b/server-base/src/main/java/org/apache/kylin/rest/service/EncodingService.java
@@ -28,6 +28,9 @@ import org.apache.kylin.dimension.FixedLenHexDimEnc;
 import org.apache.kylin.dimension.IntegerDimEnc;
 import org.apache.kylin.dimension.TimeDimEnc;
 import org.apache.kylin.metadata.datatype.DataType;
+import org.apache.kylin.rest.exception.BadRequestException;
+import org.apache.kylin.rest.msg.Message;
+import org.apache.kylin.rest.msg.MsgPicker;
 import org.springframework.stereotype.Component;
 
 import com.google.common.collect.Lists;
@@ -36,6 +39,8 @@ import com.google.common.collect.Lists;
 public class EncodingService extends BasicService {
 
     public List<String> getValidEncodings(DataType dataType) {
+        Message msg = MsgPicker.getMsg();
+
         if (dataType.isIntegerFamily()) {
             return Lists.newArrayList(BooleanDimEnc.ENCODING_NAME, DateDimEnc.ENCODING_NAME, TimeDimEnc.ENCODING_NAME, DictionaryDimEnc.ENCODING_NAME, IntegerDimEnc.ENCODING_NAME);
         } else if (dataType.isNumberFamily()) { //numbers include integers
@@ -46,7 +51,7 @@ public class EncodingService extends BasicService {
             return Lists.newArrayList(BooleanDimEnc.ENCODING_NAME, DictionaryDimEnc.ENCODING_NAME, FixedLenDimEnc.ENCODING_NAME, //
                     FixedLenHexDimEnc.ENCODING_NAME, IntegerDimEnc.ENCODING_NAME);
         } else {
-            throw new IllegalArgumentException("can't provide valid encodings for datatype:" + dataType);
+            throw new BadRequestException(String.format(msg.getVALID_ENCODING_NOT_AVAILABLE(), dataType));
         }
     }
 

http://git-wip-us.apache.org/repos/asf/kylin/blob/73a78dbe/server-base/src/main/java/org/apache/kylin/rest/service/ExtFilterService.java
----------------------------------------------------------------------
diff --git a/server-base/src/main/java/org/apache/kylin/rest/service/ExtFilterService.java b/server-base/src/main/java/org/apache/kylin/rest/service/ExtFilterService.java
index 0a8439d..90dac27 100644
--- a/server-base/src/main/java/org/apache/kylin/rest/service/ExtFilterService.java
+++ b/server-base/src/main/java/org/apache/kylin/rest/service/ExtFilterService.java
@@ -23,7 +23,9 @@ import java.util.List;
 
 import org.apache.kylin.metadata.model.ExternalFilterDesc;
 import org.apache.kylin.rest.constant.Constant;
-import org.apache.kylin.rest.exception.InternalErrorException;
+import org.apache.kylin.rest.exception.BadRequestException;
+import org.apache.kylin.rest.msg.Message;
+import org.apache.kylin.rest.msg.MsgPicker;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.security.access.prepost.PreAuthorize;
@@ -36,16 +38,20 @@ public class ExtFilterService extends BasicService {
 
     @PreAuthorize(Constant.ACCESS_HAS_ROLE_ADMIN)
     public void saveExternalFilter(ExternalFilterDesc desc) throws IOException {
+        Message msg = MsgPicker.getMsg();
+
         if (getMetadataManager().getExtFilterDesc(desc.getName()) != null) {
-            throw new InternalErrorException("The filter named " + desc.getName() + " already exists");
+            throw new BadRequestException(String.format(msg.getFILTER_ALREADY_EXIST(), desc.getName()));
         }
         getMetadataManager().saveExternalFilter(desc);
     }
 
     @PreAuthorize(Constant.ACCESS_HAS_ROLE_ADMIN)
     public void updateExternalFilter(ExternalFilterDesc desc) throws IOException {
+        Message msg = MsgPicker.getMsg();
+
         if (getMetadataManager().getExtFilterDesc(desc.getName()) == null) {
-            throw new InternalErrorException("The filter named " + desc.getName() + " does not exists");
+            throw new BadRequestException(String.format(msg.getFILTER_NOT_FOUND(), desc.getName()));
         }
         getMetadataManager().saveExternalFilter(desc);
     }

http://git-wip-us.apache.org/repos/asf/kylin/blob/73a78dbe/server-base/src/main/java/org/apache/kylin/rest/service/JobService.java
----------------------------------------------------------------------
diff --git a/server-base/src/main/java/org/apache/kylin/rest/service/JobService.java b/server-base/src/main/java/org/apache/kylin/rest/service/JobService.java
index 56a148d..4e5f80f 100644
--- a/server-base/src/main/java/org/apache/kylin/rest/service/JobService.java
+++ b/server-base/src/main/java/org/apache/kylin/rest/service/JobService.java
@@ -6,9 +6,9 @@
  * 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.
@@ -45,7 +45,6 @@ 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.JobException;
 import org.apache.kylin.job.exception.SchedulerException;
 import org.apache.kylin.job.execution.AbstractExecutable;
 import org.apache.kylin.job.execution.DefaultChainedExecutable;
@@ -56,6 +55,8 @@ import org.apache.kylin.metadata.model.SegmentStatusEnum;
 import org.apache.kylin.metadata.realization.RealizationStatusEnum;
 import org.apache.kylin.rest.constant.Constant;
 import org.apache.kylin.rest.exception.BadRequestException;
+import org.apache.kylin.rest.msg.Message;
+import org.apache.kylin.rest.msg.MsgPicker;
 import org.apache.kylin.source.ISource;
 import org.apache.kylin.source.SourceFactory;
 import org.apache.kylin.source.SourcePartition;
@@ -63,12 +64,12 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.InitializingBean;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.context.annotation.EnableAspectJAutoProxy;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.stereotype.Component;
 
 import com.google.common.base.Function;
-import com.google.common.base.Preconditions;
 import com.google.common.base.Predicate;
 import com.google.common.base.Predicates;
 import com.google.common.collect.FluentIterable;
@@ -88,6 +89,7 @@ public class JobService extends BasicService implements InitializingBean {
     private JobLock jobLock;
 
     @Autowired
+    @Qualifier("accessService")
     private AccessService accessService;
 
     /*
@@ -148,54 +150,59 @@ public class JobService extends BasicService implements InitializingBean {
         return states;
     }
 
-    private long getTimeStartInMillis(Calendar calendar, JobTimeFilterEnum timeFilter) {
-        switch (timeFilter) {
-        case LAST_ONE_DAY:
-            calendar.add(Calendar.DAY_OF_MONTH, -1);
-            return calendar.getTimeInMillis();
-        case LAST_ONE_WEEK:
-            calendar.add(Calendar.WEEK_OF_MONTH, -1);
-            return calendar.getTimeInMillis();
-        case LAST_ONE_MONTH:
-            calendar.add(Calendar.MONTH, -1);
-            return calendar.getTimeInMillis();
-        case LAST_ONE_YEAR:
-            calendar.add(Calendar.YEAR, -1);
-            return calendar.getTimeInMillis();
-        case ALL:
-            return 0;
-        default:
-            throw new RuntimeException("illegal timeFilter for job history:" + timeFilter);
+    private ExecutableState parseToExecutableState(JobStatusEnum status) {
+        Message msg = MsgPicker.getMsg();
+
+        switch (status) {
+            case DISCARDED:
+                return ExecutableState.DISCARDED;
+            case ERROR:
+                return ExecutableState.ERROR;
+            case FINISHED:
+                return ExecutableState.SUCCEED;
+            case NEW:
+                return ExecutableState.READY;
+            case PENDING:
+                return ExecutableState.READY;
+            case RUNNING:
+                return ExecutableState.RUNNING;
+            case STOPPED:
+                return ExecutableState.STOPPED;
+            default:
+                throw new BadRequestException(String.format(msg.getILLEGAL_EXECUTABLE_STATE(), status));
         }
     }
 
-    private ExecutableState parseToExecutableState(JobStatusEnum status) {
-        switch (status) {
-        case DISCARDED:
-            return ExecutableState.DISCARDED;
-        case ERROR:
-            return ExecutableState.ERROR;
-        case FINISHED:
-            return ExecutableState.SUCCEED;
-        case NEW:
-            return ExecutableState.READY;
-        case PENDING:
-            return ExecutableState.READY;
-        case RUNNING:
-            return ExecutableState.RUNNING;
-        case STOPPED:
-            return ExecutableState.STOPPED;
-        default:
-            throw new RuntimeException("illegal status:" + status);
+    private long getTimeStartInMillis(Calendar calendar, JobTimeFilterEnum timeFilter) {
+        Message msg = MsgPicker.getMsg();
+
+        switch (timeFilter) {
+            case LAST_ONE_DAY:
+                calendar.add(Calendar.DAY_OF_MONTH, -1);
+                return calendar.getTimeInMillis();
+            case LAST_ONE_WEEK:
+                calendar.add(Calendar.WEEK_OF_MONTH, -1);
+                return calendar.getTimeInMillis();
+            case LAST_ONE_MONTH:
+                calendar.add(Calendar.MONTH, -1);
+                return calendar.getTimeInMillis();
+            case LAST_ONE_YEAR:
+                calendar.add(Calendar.YEAR, -1);
+                return calendar.getTimeInMillis();
+            case ALL:
+                return 0;
+            default:
+                throw new BadRequestException(String.format(msg.getILLEGAL_TIME_FILTER(), timeFilter));
         }
     }
 
     @PreAuthorize(Constant.ACCESS_HAS_ROLE_ADMIN + " or hasPermission(#cube, 'ADMINISTRATION') or hasPermission(#cube, 'OPERATION') or hasPermission(#cube, 'MANAGEMENT')")
     public JobInstance submitJob(CubeInstance cube, long startDate, long endDate, long startOffset, long endOffset, //
-            Map<Integer, Long> sourcePartitionOffsetStart, Map<Integer, Long> sourcePartitionOffsetEnd, CubeBuildTypeEnum buildType, boolean force, String submitter) throws IOException, JobException {
+                                 Map<Integer, Long> sourcePartitionOffsetStart, Map<Integer, Long> sourcePartitionOffsetEnd, CubeBuildTypeEnum buildType, boolean force, String submitter) throws IOException {
+        Message msg = MsgPicker.getMsg();
 
         if (cube.getStatus() == RealizationStatusEnum.DESCBROKEN) {
-            throw new BadRequestException("Broken cube " + cube.getName() + " can't be built");
+            throw new BadRequestException(String.format(msg.getBUILD_BROKEN_CUBE(), cube.getName()));
         }
 
         checkCubeDescSignature(cube);
@@ -216,7 +223,7 @@ public class JobService extends BasicService implements InitializingBean {
                 newSeg = getCubeManager().refreshSegment(cube, startDate, endDate, startOffset, endOffset);
                 job = EngineFactory.createBatchCubingJob(newSeg, submitter);
             } else {
-                throw new JobException("invalid build type:" + buildType);
+                throw new BadRequestException(String.format(msg.getINVALID_BUILD_TYPE(), buildType));
             }
 
             getExecutableManager().addJob(job);
@@ -246,11 +253,13 @@ public class JobService extends BasicService implements InitializingBean {
     }
 
     private void checkCubeDescSignature(CubeInstance cube) {
+        Message msg = MsgPicker.getMsg();
+
         if (!cube.getDescriptor().checkSignature())
-            throw new IllegalStateException("Inconsistent cube desc signature for " + cube.getDescriptor() + ", if it's right after a upgrade, please try 'Edit CubeDesc' to delete the 'signature' field. Or use 'bin/metastore.sh refresh-cube-signature' to batch refresh all cubes' signatures, then reload metadata to take effect");
+            throw new BadRequestException(String.format(msg.getINCONSISTENT_CUBE_DESC_SIGNATURE(), cube.getDescriptor()));
     }
 
-    public JobInstance getJobInstance(String uuid) throws IOException, JobException {
+    public JobInstance getJobInstance(String uuid) {
         return getSingleJobInstance(getExecutableManager().getJob(uuid));
     }
 
@@ -258,11 +267,16 @@ public class JobService extends BasicService implements InitializingBean {
         return getExecutableManager().getOutput(id);
     }
 
-    private JobInstance getSingleJobInstance(AbstractExecutable job) {
+    protected JobInstance getSingleJobInstance(AbstractExecutable job) {
+        Message msg = MsgPicker.getMsg();
+
         if (job == null) {
             return null;
         }
-        Preconditions.checkState(job instanceof CubingJob, "illegal job type, id:" + job.getId());
+        if (!(job instanceof CubingJob)) {
+            throw new BadRequestException(String.format(msg.getILLEGAL_JOB_TYPE(), job.getId()));
+        }
+
         CubingJob cubeJob = (CubingJob) job;
         final JobInstance result = new JobInstance();
         result.setName(job.getName());
@@ -283,17 +297,17 @@ public class JobService extends BasicService implements InitializingBean {
     }
 
     @PreAuthorize(Constant.ACCESS_HAS_ROLE_ADMIN + " or hasPermission(#job, 'ADMINISTRATION') or hasPermission(#job, 'OPERATION') or hasPermission(#job, 'MANAGEMENT')")
-    public void resumeJob(JobInstance job) throws IOException, JobException {
+    public void resumeJob(JobInstance job) {
         getExecutableManager().resumeJob(job.getId());
     }
 
     @PreAuthorize(Constant.ACCESS_HAS_ROLE_ADMIN + " or hasPermission(#job, 'ADMINISTRATION') or hasPermission(#job, 'OPERATION') or hasPermission(#job, 'MANAGEMENT')")
-    public void rollbackJob(JobInstance job, String stepId) throws IOException, JobException {
+    public void rollbackJob(JobInstance job, String stepId) {
         getExecutableManager().rollbackJob(job.getId(), stepId);
     }
 
     @PreAuthorize(Constant.ACCESS_HAS_ROLE_ADMIN + " or hasPermission(#job, 'ADMINISTRATION') or hasPermission(#job, 'OPERATION') or hasPermission(#job, 'MANAGEMENT')")
-    public JobInstance cancelJob(JobInstance job) throws IOException, JobException {
+    public JobInstance cancelJob(JobInstance job) throws IOException {
         if (null == job.getRelatedCube() || null == getCubeManager().getCube(job.getRelatedCube())) {
             getExecutableManager().discardJob(job.getId());
             return job;
@@ -316,13 +330,13 @@ public class JobService extends BasicService implements InitializingBean {
     }
 
     @PreAuthorize(Constant.ACCESS_HAS_ROLE_ADMIN + " or hasPermission(#job, 'ADMINISTRATION') or hasPermission(#job, 'OPERATION') or hasPermission(#job, 'MANAGEMENT')")
-    public JobInstance pauseJob(JobInstance job) throws IOException, JobException {
+    public JobInstance pauseJob(JobInstance job) {
         getExecutableManager().pauseJob(job.getId());
         return job;
     }
 
     @PreAuthorize(Constant.ACCESS_HAS_ROLE_ADMIN + " or hasPermission(#job, 'ADMINISTRATION') or hasPermission(#job, 'OPERATION') or hasPermission(#job, 'MANAGEMENT')")
-    public void dropJob(JobInstance job) throws IOException, JobException {
+    public void dropJob(JobInstance job) throws IOException {
         cancelJob(job);
         getExecutableManager().deleteJob(job.getId());
     }
@@ -331,7 +345,7 @@ public class JobService extends BasicService implements InitializingBean {
      * currently only support substring match
      * @return
      */
-    public List<JobInstance> searchJobs(final String cubeNameSubstring, final String projectName, final List<JobStatusEnum> statusList, final Integer limitValue, final Integer offsetValue, final JobTimeFilterEnum timeFilter) throws IOException, JobException {
+    public List<JobInstance> searchJobs(final String cubeNameSubstring, final String projectName, final List<JobStatusEnum> statusList, final Integer limitValue, final Integer offsetValue, final JobTimeFilterEnum timeFilter) {
         Integer limit = (null == limitValue) ? 30 : limitValue;
         Integer offset = (null == offsetValue) ? 0 : offsetValue;
         List<JobInstance> jobs = searchJobs(cubeNameSubstring, projectName, statusList, timeFilter);
@@ -348,7 +362,7 @@ public class JobService extends BasicService implements InitializingBean {
         return jobs.subList(offset, offset + limit);
     }
 
-    private List<JobInstance> searchJobs(final String cubeNameSubstring, final String projectName, final List<JobStatusEnum> statusList, final JobTimeFilterEnum timeFilter) {
+    public List<JobInstance> searchJobs(final String cubeNameSubstring, final String projectName, final List<JobStatusEnum> statusList, final JobTimeFilterEnum timeFilter) {
         Calendar calendar = Calendar.getInstance();
         calendar.setTime(new Date());
         long timeStartInMillis = getTimeStartInMillis(calendar, timeFilter);

http://git-wip-us.apache.org/repos/asf/kylin/blob/73a78dbe/server-base/src/main/java/org/apache/kylin/rest/service/KafkaConfigService.java
----------------------------------------------------------------------
diff --git a/server-base/src/main/java/org/apache/kylin/rest/service/KafkaConfigService.java b/server-base/src/main/java/org/apache/kylin/rest/service/KafkaConfigService.java
index f7fc49b..0dbe6f2 100644
--- a/server-base/src/main/java/org/apache/kylin/rest/service/KafkaConfigService.java
+++ b/server-base/src/main/java/org/apache/kylin/rest/service/KafkaConfigService.java
@@ -23,7 +23,9 @@ import java.util.ArrayList;
 import java.util.List;
 
 import org.apache.kylin.rest.constant.Constant;
-import org.apache.kylin.rest.exception.InternalErrorException;
+import org.apache.kylin.rest.exception.BadRequestException;
+import org.apache.kylin.rest.msg.Message;
+import org.apache.kylin.rest.msg.MsgPicker;
 import org.apache.kylin.source.kafka.config.KafkaConfig;
 import org.springframework.security.access.prepost.PostFilter;
 import org.springframework.stereotype.Component;
@@ -66,8 +68,10 @@ public class KafkaConfigService extends BasicService {
     }
 
     public KafkaConfig createKafkaConfig(KafkaConfig config) throws IOException {
+        Message msg = MsgPicker.getMsg();
+
         if (getKafkaManager().getKafkaConfig(config.getName()) != null) {
-            throw new InternalErrorException("The kafkaConfig named " + config.getName() + " already exists");
+            throw new BadRequestException(String.format(msg.getKAFKA_CONFIG_ALREADY_EXIST(), config.getName()));
         }
         getKafkaManager().createKafkaConfig(config.getName(), config);
         return config;

http://git-wip-us.apache.org/repos/asf/kylin/blob/73a78dbe/server-base/src/main/java/org/apache/kylin/rest/service/ModelService.java
----------------------------------------------------------------------
diff --git a/server-base/src/main/java/org/apache/kylin/rest/service/ModelService.java b/server-base/src/main/java/org/apache/kylin/rest/service/ModelService.java
index 614f0c9..9401624 100644
--- a/server-base/src/main/java/org/apache/kylin/rest/service/ModelService.java
+++ b/server-base/src/main/java/org/apache/kylin/rest/service/ModelService.java
@@ -18,10 +18,6 @@
 
 package org.apache.kylin.rest.service;
 
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
 import org.apache.kylin.common.util.HadoopUtil;
 import org.apache.kylin.cube.model.CubeDesc;
 import org.apache.kylin.metadata.model.DataModelDesc;
@@ -30,11 +26,16 @@ import org.apache.kylin.rest.constant.Constant;
 import org.apache.kylin.rest.exception.InternalErrorException;
 import org.apache.kylin.rest.security.AclPermission;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.security.access.prepost.PostFilter;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.stereotype.Component;
 
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * @author jiazhong
  */
@@ -42,8 +43,13 @@ import org.springframework.stereotype.Component;
 public class ModelService extends BasicService {
 
     @Autowired
+    @Qualifier("accessService")
     private AccessService accessService;
 
+    @Autowired
+    @Qualifier("cubeMgmtService")
+    private CubeService cubeService;
+
     @PostFilter(Constant.ACCESS_POST_FILTER_READ)
     public List<DataModelDesc> listAllModels(final String modelName, final String projectName) throws IOException {
         List<DataModelDesc> models;
@@ -139,4 +145,10 @@ public class ModelService extends BasicService {
         tableName = dbTableName[0] + "." + dbTableName[1];
         return getMetadataManager().getModelsUsingTable(tableName, projectName);
     }
+
+    public boolean checkNameAvailability(String modelName) throws IOException {
+        List<DataModelDesc> models = listAllModels(modelName, null);
+
+        return models.isEmpty();
+    }
 }

http://git-wip-us.apache.org/repos/asf/kylin/blob/73a78dbe/server-base/src/main/java/org/apache/kylin/rest/service/ModelServiceV2.java
----------------------------------------------------------------------
diff --git a/server-base/src/main/java/org/apache/kylin/rest/service/ModelServiceV2.java b/server-base/src/main/java/org/apache/kylin/rest/service/ModelServiceV2.java
new file mode 100644
index 0000000..1628964
--- /dev/null
+++ b/server-base/src/main/java/org/apache/kylin/rest/service/ModelServiceV2.java
@@ -0,0 +1,351 @@
+/*
+ * 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.service;
+
+import static org.apache.kylin.metadata.model.DataModelDesc.STATUS_DRAFT;
+import static org.apache.kylin.rest.controller2.ModelControllerV2.VALID_MODELNAME;
+
+import java.io.IOException;
+import java.util.ArrayList;
+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.cube.CubeInstance;
+import org.apache.kylin.cube.model.CubeDesc;
+import org.apache.kylin.metadata.model.DataModelDesc;
+import org.apache.kylin.metadata.model.JoinsTree;
+import org.apache.kylin.metadata.model.ModelDimensionDesc;
+import org.apache.kylin.metadata.model.TblColRef;
+import org.apache.kylin.metadata.project.ProjectInstance;
+import org.apache.kylin.rest.constant.Constant;
+import org.apache.kylin.rest.exception.BadRequestException;
+import org.apache.kylin.rest.exception.ForbiddenException;
+import org.apache.kylin.rest.msg.Message;
+import org.apache.kylin.rest.msg.MsgPicker;
+import org.apache.kylin.rest.security.AclPermission;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Component;
+
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+
+/**
+ * Created by luwei on 17-4-19.
+ */
+@Component("modelMgmtServiceV2")
+public class ModelServiceV2 extends ModelService {
+
+    private static final Logger logger = LoggerFactory.getLogger(ModelServiceV2.class);
+
+    @Autowired
+    @Qualifier("accessService")
+    private AccessService accessService;
+
+    @Autowired
+    @Qualifier("cubeMgmtServiceV2")
+    private CubeServiceV2 cubeServiceV2;
+
+    public DataModelDesc createModelDesc(String projectName, DataModelDesc desc) throws IOException {
+        Message msg = MsgPicker.getMsg();
+
+        if (getMetadataManager().getDataModelDesc(desc.getName()) != null) {
+            throw new BadRequestException(String.format(msg.getDUPLICATE_MODEL_NAME(), desc.getName()));
+        }
+        DataModelDesc createdDesc = null;
+        String owner = SecurityContextHolder.getContext().getAuthentication().getName();
+        createdDesc = getMetadataManager().createDataModelDesc(desc, projectName, owner);
+
+        accessService.init(createdDesc, AclPermission.ADMINISTRATION);
+        ProjectInstance project = getProjectManager().getProject(projectName);
+        accessService.inherit(createdDesc, project);
+        return createdDesc;
+    }
+
+    @PreAuthorize(Constant.ACCESS_HAS_ROLE_ADMIN + " or hasPermission(#desc, 'ADMINISTRATION') or hasPermission(#desc, 'MANAGEMENT')")
+    public void dropModel(DataModelDesc desc) throws IOException {
+        Message msg = MsgPicker.getMsg();
+
+        //check cube desc exist
+        List<CubeDesc> cubeDescs = getCubeDescManager().listAllDesc();
+        for (CubeDesc cubeDesc : cubeDescs) {
+            if (cubeDesc.getModelName().equals(desc.getName())) {
+                throw new BadRequestException(String.format(msg.getDROP_REFERENCED_MODEL(), cubeDesc.getName()));
+            }
+        }
+
+        getMetadataManager().dropModel(desc);
+
+        accessService.clean(desc, true);
+    }
+
+    public Map<TblColRef, Set<CubeInstance>> getUsedDimCols(String modelName) {
+        Map<TblColRef, Set<CubeInstance>> ret = Maps.newHashMap();
+        List<CubeInstance> cubeInstances = cubeServiceV2.listAllCubes(null, null, modelName);
+        for (CubeInstance cubeInstance : cubeInstances) {
+            CubeDesc cubeDesc = cubeInstance.getDescriptor();
+            for (TblColRef tblColRef : cubeDesc.listDimensionColumnsIncludingDerived()) {
+                if (ret.containsKey(tblColRef)) {
+                    ret.get(tblColRef).add(cubeInstance);
+                } else {
+                    Set<CubeInstance> set = Sets.newHashSet(cubeInstance);
+                    ret.put(tblColRef, set);
+                }
+            }
+        }
+        return ret;
+    }
+
+    public Map<TblColRef, Set<CubeInstance>> getUsedNonDimCols(String modelName) {
+        Map<TblColRef, Set<CubeInstance>> ret = Maps.newHashMap();
+        List<CubeInstance> cubeInstances = cubeServiceV2.listAllCubes(null, null, modelName);
+        for (CubeInstance cubeInstance : cubeInstances) {
+            CubeDesc cubeDesc = cubeInstance.getDescriptor();
+            Set<TblColRef> tblColRefs = Sets.newHashSet(cubeDesc.listAllColumns());//make a copy
+            tblColRefs.removeAll(cubeDesc.listDimensionColumnsIncludingDerived());
+            for (TblColRef tblColRef : tblColRefs) {
+                if (ret.containsKey(tblColRef)) {
+                    ret.get(tblColRef).add(cubeInstance);
+                } else {
+                    Set<CubeInstance> set = Sets.newHashSet(cubeInstance);
+                    ret.put(tblColRef, set);
+                }
+            }
+        }
+        return ret;
+    }
+
+    public boolean validate(DataModelDesc dataModelDesc) throws IOException {
+        Message msg = MsgPicker.getMsg();
+
+        dataModelDesc.init(getConfig(), getMetadataManager().getAllTablesMap(), getMetadataManager().getCcInfoMap());
+
+        List<String> dimCols = new ArrayList<String>();
+        List<String> dimAndMCols = new ArrayList<String>();
+
+        List<ModelDimensionDesc> dimensions = dataModelDesc.getDimensions();
+        String[] measures = dataModelDesc.getMetrics();
+
+        for (ModelDimensionDesc dim : dimensions) {
+            String table = dim.getTable();
+            for (String c : dim.getColumns()) {
+                dimCols.add(table + "." + c);
+            }
+        }
+
+        dimAndMCols.addAll(dimCols);
+
+        for (String measure : measures) {
+            dimAndMCols.add(measure);
+        }
+
+        String modelName = dataModelDesc.getName();
+        Set<TblColRef> usedDimCols = getUsedDimCols(modelName).keySet();
+        Set<TblColRef> usedNonDimCols = getUsedNonDimCols(modelName).keySet();
+
+        for (TblColRef tblColRef : usedDimCols) {
+            if (!dimCols.contains(tblColRef.getTableAlias() + "." + tblColRef.getName()))
+                return false;
+        }
+
+        for (TblColRef tblColRef : usedNonDimCols) {
+            if (!dimAndMCols.contains(tblColRef.getTableAlias() + "." + tblColRef.getName()))
+                return false;
+        }
+
+        DataModelDesc originDataModelDesc = listAllModels(modelName, null).get(0);
+
+        if (!dataModelDesc.getRootFactTable().equals(originDataModelDesc.getRootFactTable()))
+            return false;
+
+        JoinsTree joinsTree = dataModelDesc.getJoinsTree(), originJoinsTree = originDataModelDesc.getJoinsTree();
+        if (joinsTree.matchNum(originJoinsTree) != originDataModelDesc.getJoinTables().length + 1)
+            return false;
+
+        return true;
+    }
+
+    public void validateModelDesc(DataModelDesc modelDesc) {
+        Message msg = MsgPicker.getMsg();
+
+        if (modelDesc == null) {
+            throw new BadRequestException(msg.getINVALID_MODEL_DEFINITION());
+        }
+
+        String modelName = modelDesc.getName();
+
+        if (StringUtils.isEmpty(modelName)) {
+            logger.info("Model name should not be empty.");
+            throw new BadRequestException(msg.getEMPTY_MODEL_NAME());
+        }
+        if (!StringUtils.containsOnly(modelName, VALID_MODELNAME)) {
+            logger.info("Invalid Model name {}, only letters, numbers and underline supported.", modelDesc.getName());
+            throw new BadRequestException(String.format(msg.getINVALID_MODEL_NAME(), modelName));
+        }
+    }
+
+    public boolean unifyModelDesc(DataModelDesc modelDesc, boolean isDraft) throws IOException {
+        Message msg = MsgPicker.getMsg();
+
+        boolean createNew = false;
+        String modelName = modelDesc.getName();
+        String originName = null;   // for draft rename check
+        if (modelDesc.getUuid() != null) {
+            originName = getNameByUuid(modelDesc.getUuid());
+        }
+
+        if (!isDraft) { // save as official model
+            if (modelDesc.getStatus() != null && modelDesc.getStatus().equals(STATUS_DRAFT)) {  // from draft
+                if (originName == null) {
+                    throw new BadRequestException(msg.getORIGIN_MODEL_NOT_FOUND());
+                }
+                originName = originName.substring(0, originName.lastIndexOf("_draft"));
+                if (!originName.equals(modelName)) {     // if rename draft
+                    DataModelDesc parentDesc = getMetadataManager().getDataModelDesc(originName);
+                    if (parentDesc == null) {   // only allow rename when official model has not been saved
+                        createNew = true;
+                        dropModelByUuid(modelDesc.getUuid());
+                        modelDesc.setStatus(null);
+                        modelDesc.setLastModified(0);
+                        modelDesc.setUuid(UUID.randomUUID().toString());
+                    } else {
+                        throw new BadRequestException(msg.getMODEL_RENAME());
+                    }
+                } else {    // without rename draft
+                    modelDesc.setStatus(null);
+                    DataModelDesc parentDesc = getMetadataManager().getDataModelDesc(modelName);
+                    if (parentDesc == null) {   // official model doesn't exist, create new one
+                        createNew = true;
+                        modelDesc.setLastModified(0);
+                        modelDesc.setUuid(UUID.randomUUID().toString());
+                    } else {    // update existing
+                        modelDesc.setLastModified(parentDesc.getLastModified());
+                        modelDesc.setUuid(parentDesc.getUuid());
+                    }
+                }
+            } else {    // from official
+                if (originName == null) {   // official model doesn't exist, create new one
+                    createNew = true;
+                    modelDesc.setLastModified(0);
+                    modelDesc.setUuid(UUID.randomUUID().toString());
+                } else {
+                    if (!originName.equals(modelName)) {    // do not allow official model rename
+                        throw new BadRequestException(msg.getMODEL_RENAME());
+                    }
+                }
+            }
+        } else {    // save as draft model
+            if (modelDesc.getStatus() == null) {    // from official
+                modelName += "_draft";
+                modelDesc.setName(modelName);
+                modelDesc.setStatus(STATUS_DRAFT);
+                DataModelDesc draftDesc = getMetadataManager().getDataModelDesc(modelName);
+                if (draftDesc == null) {    // draft model doesn't exist, create new one
+                    createNew = true;
+                    modelDesc.setLastModified(0);
+                    modelDesc.setUuid(UUID.randomUUID().toString());
+                } else if (draftDesc.getStatus() != null && draftDesc.getStatus().equals(STATUS_DRAFT)) {   // update existing
+                    modelDesc.setLastModified(draftDesc.getLastModified());
+                    modelDesc.setUuid(draftDesc.getUuid());
+                } else {    // already exist an official draft with name ends with '_draft'
+                    throw new BadRequestException(String.format(msg.getNON_DRAFT_MODEL_ALREADY_EXIST(), modelName));
+                }
+            } else if (modelDesc.getStatus().equals(STATUS_DRAFT)) {    // from draft
+                if (originName == null) {
+                    throw new BadRequestException(msg.getORIGIN_MODEL_NOT_FOUND());
+                }
+                originName = originName.substring(0, originName.lastIndexOf("_draft"));
+                if (!originName.equals(modelName)) {    // if rename draft
+                    DataModelDesc parentDesc = getMetadataManager().getDataModelDesc(originName);
+                    if (parentDesc == null) {   // only allow rename when official model has not been saved
+                        createNew = true;
+                        dropModelByUuid(modelDesc.getUuid());
+                        modelName += "_draft";
+                        modelDesc.setName(modelName);
+                        modelDesc.setStatus(STATUS_DRAFT);
+                        modelDesc.setLastModified(0);
+                        modelDesc.setUuid(UUID.randomUUID().toString());
+                    } else {
+                        throw new BadRequestException(msg.getMODEL_RENAME());
+                    }
+                } else {    // without rename draft
+                    modelName += "_draft";
+                    modelDesc.setName(modelName);
+                }
+            }
+        }
+        return createNew;
+    }
+
+    public String getNameByUuid(String uuid) {
+        List<DataModelDesc> models = getMetadataManager().getModels();
+        for (DataModelDesc model : models) {
+            if (model.getUuid().equals(uuid)) {
+                return model.getName();
+            }
+        }
+        return null;
+    }
+
+    public void dropModelByUuid(String uuid) throws IOException {
+        List<DataModelDesc> models = getMetadataManager().getModels();
+        for (DataModelDesc model : models) {
+            if (model.getUuid().equals(uuid)) {
+                dropModel(model);
+            }
+        }
+    }
+
+    public DataModelDesc updateModelToResourceStore(DataModelDesc modelDesc, String projectName, boolean createNew, boolean isDraft) throws IOException {
+        Message msg = MsgPicker.getMsg();
+
+        if (createNew) {
+            createModelDesc(projectName, modelDesc);
+        } else {
+            try {
+                if (!isDraft && !validate(modelDesc)) {
+                    throw new BadRequestException(msg.getUPDATE_MODEL_KEY_FIELD());
+                }
+                modelDesc = updateModelAndDesc(modelDesc);
+            } catch (AccessDeniedException accessDeniedException) {
+                throw new ForbiddenException(msg.getUPDATE_MODEL_NO_RIGHT());
+            }
+
+            if (!modelDesc.getError().isEmpty()) {
+                throw new BadRequestException(String.format(msg.getBROKEN_MODEL_DESC(), modelDesc.getName()));
+            }
+        }
+
+        if (!isDraft) {
+            DataModelDesc draftDesc = getMetadataManager().getDataModelDesc(modelDesc.getName() + "_draft");
+            if (null != draftDesc && draftDesc.getStatus() != null && draftDesc.getStatus().equals(STATUS_DRAFT)) {
+                dropModel(draftDesc);
+            }
+        }
+        return modelDesc;
+    }
+}

http://git-wip-us.apache.org/repos/asf/kylin/blob/73a78dbe/server-base/src/main/java/org/apache/kylin/rest/service/ProjectService.java
----------------------------------------------------------------------
diff --git a/server-base/src/main/java/org/apache/kylin/rest/service/ProjectService.java b/server-base/src/main/java/org/apache/kylin/rest/service/ProjectService.java
index 8c0cf7a..cf86d3e 100644
--- a/server-base/src/main/java/org/apache/kylin/rest/service/ProjectService.java
+++ b/server-base/src/main/java/org/apache/kylin/rest/service/ProjectService.java
@@ -31,6 +31,7 @@ import org.apache.kylin.rest.security.AclPermission;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.security.access.prepost.PostFilter;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.security.core.context.SecurityContextHolder;
@@ -46,6 +47,7 @@ public class ProjectService extends BasicService {
     private static final Logger logger = LoggerFactory.getLogger(ProjectService.class);
 
     @Autowired
+    @Qualifier("accessService")
     private AccessService accessService;
 
     @PreAuthorize(Constant.ACCESS_HAS_ROLE_ADMIN)

http://git-wip-us.apache.org/repos/asf/kylin/blob/73a78dbe/server-base/src/main/java/org/apache/kylin/rest/service/ProjectServiceV2.java
----------------------------------------------------------------------
diff --git a/server-base/src/main/java/org/apache/kylin/rest/service/ProjectServiceV2.java b/server-base/src/main/java/org/apache/kylin/rest/service/ProjectServiceV2.java
new file mode 100644
index 0000000..fb1ac94
--- /dev/null
+++ b/server-base/src/main/java/org/apache/kylin/rest/service/ProjectServiceV2.java
@@ -0,0 +1,89 @@
+/*
+ * 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.service;
+
+import java.io.IOException;
+import java.util.LinkedHashMap;
+import java.util.List;
+
+import org.apache.kylin.metadata.project.ProjectInstance;
+import org.apache.kylin.rest.constant.Constant;
+import org.apache.kylin.rest.exception.BadRequestException;
+import org.apache.kylin.rest.msg.Message;
+import org.apache.kylin.rest.msg.MsgPicker;
+import org.apache.kylin.rest.security.AclPermission;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Component;
+
+/**
+ * Created by luwei on 17-4-20.
+ */
+@Component("projectServiceV2")
+public class ProjectServiceV2 extends ProjectService {
+
+    private static final Logger logger = LoggerFactory.getLogger(ProjectServiceV2.class);
+
+    @Autowired
+    @Qualifier("accessService")
+    private AccessService accessService;
+
+    @PreAuthorize(Constant.ACCESS_HAS_ROLE_ADMIN)
+    public ProjectInstance createProject(ProjectInstance newProject) throws IOException {
+        Message msg = MsgPicker.getMsg();
+
+        String projectName = newProject.getName();
+        String description = newProject.getDescription();
+        LinkedHashMap<String, String> overrideProps = newProject.getOverrideKylinProps();
+
+        ProjectInstance currentProject = getProjectManager().getProject(projectName);
+
+        if (currentProject != null) {
+            throw new BadRequestException(String.format(msg.getPROJECT_ALREADY_EXIST(), projectName));
+        }
+        String owner = SecurityContextHolder.getContext().getAuthentication().getName();
+        ProjectInstance createdProject = getProjectManager().createProject(projectName, owner, description, overrideProps);
+        accessService.init(createdProject, AclPermission.ADMINISTRATION);
+        logger.debug("New project created.");
+
+        return createdProject;
+    }
+
+    @PreAuthorize(Constant.ACCESS_HAS_ROLE_ADMIN + " or hasPermission(#project, 'ADMINISTRATION') or hasPermission(#project, 'MANAGEMENT')")
+    public void deleteProject(String projectName, ProjectInstance project) throws IOException {
+        getProjectManager().dropProject(projectName);
+
+        accessService.clean(project, true);
+    }
+
+    public String getProjectOfModel(String modelName) {
+        List<ProjectInstance> projectInstances = listProjects(null, null);
+        for (ProjectInstance projectInstance : projectInstances) {
+            if (projectInstance.containsModel(modelName))
+                return projectInstance.getName();
+        }
+        return null;
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/kylin/blob/73a78dbe/server-base/src/main/java/org/apache/kylin/rest/service/QueryService.java
----------------------------------------------------------------------
diff --git a/server-base/src/main/java/org/apache/kylin/rest/service/QueryService.java b/server-base/src/main/java/org/apache/kylin/rest/service/QueryService.java
index 9eab5a3..c4b5c0d 100644
--- a/server-base/src/main/java/org/apache/kylin/rest/service/QueryService.java
+++ b/server-base/src/main/java/org/apache/kylin/rest/service/QueryService.java
@@ -86,6 +86,7 @@ import org.apache.kylin.storage.hybrid.HybridInstance;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.security.access.AccessDeniedException;
 import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.core.context.SecurityContextHolder;
@@ -114,15 +115,16 @@ public class QueryService extends BasicService {
     public static final String EXCEPTION_QUERY_CACHE = "ExceptionQueryCache";
 
     private final Serializer<Query[]> querySerializer = new Serializer<Query[]>(Query[].class);
-    private final BadQueryDetector badQueryDetector = new BadQueryDetector();
+    protected final BadQueryDetector badQueryDetector = new BadQueryDetector();
 
     private final StorageURL hbaseUrl;
     private final String userTableName;
 
     @Autowired
-    private CacheManager cacheManager;
+    protected CacheManager cacheManager;
 
     @Autowired
+    @Qualifier("cacheService")
     private CacheService cacheService;
 
     @Autowired
@@ -524,7 +526,7 @@ public class QueryService extends BasicService {
         return tableMetas;
     }
 
-    private void processStatementAttr(Statement s, SQLRequest sqlRequest) throws SQLException {
+    protected void processStatementAttr(Statement s, SQLRequest sqlRequest) throws SQLException {
         Integer statementMaxRows = BackdoorToggles.getStatementMaxRows();
         if (statementMaxRows != null) {
             logger.info("Setting current statement's max rows to {}", statementMaxRows);
@@ -672,7 +674,7 @@ public class QueryService extends BasicService {
         }
     }
 
-    private int getInt(String content) {
+    protected int getInt(String content) {
         try {
             return Integer.parseInt(content);
         } catch (Exception e) {
@@ -680,7 +682,7 @@ public class QueryService extends BasicService {
         }
     }
 
-    private short getShort(String content) {
+    protected short getShort(String content) {
         try {
             return Short.parseShort(content);
         } catch (Exception e) {
@@ -688,7 +690,7 @@ public class QueryService extends BasicService {
         }
     }
 
-    private static void close(ResultSet resultSet, Statement stat, Connection conn) {
+    protected static void close(ResultSet resultSet, Statement stat, Connection conn) {
         OLAPContext.clearParameter();
         DBUtils.closeQuietly(resultSet);
         DBUtils.closeQuietly(stat);


Mime
View raw message