/*
* Copyright 2012 LinkedIn Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package azkaban.webapp.servlet;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Writer;
import java.security.AccessControlException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import azkaban.executor.ExecutableFlow;
import azkaban.executor.ExecutableJobInfo;
import azkaban.executor.ExecutorManagerAdapter;
import azkaban.executor.ExecutorManagerException;
import azkaban.executor.Status;
import azkaban.flow.Edge;
import azkaban.flow.Flow;
import azkaban.flow.FlowProps;
import azkaban.flow.Node;
import azkaban.project.Project;
import azkaban.project.ProjectFileHandler;
import azkaban.project.ProjectLogEvent;
import azkaban.project.ProjectManager;
import azkaban.project.ProjectManagerException;
import azkaban.project.ProjectWhitelist;
import azkaban.project.validator.ValidationReport;
import azkaban.project.validator.ValidatorConfigs;
import azkaban.scheduler.Schedule;
import azkaban.scheduler.ScheduleManager;
import azkaban.scheduler.ScheduleManagerException;
import azkaban.server.session.Session;
import azkaban.user.Permission;
import azkaban.user.Permission.Type;
import azkaban.user.Role;
import azkaban.user.User;
import azkaban.user.UserManager;
import azkaban.utils.JSONUtils;
import azkaban.utils.Pair;
import azkaban.utils.Props;
import azkaban.utils.PropsUtils;
import azkaban.utils.Utils;
import azkaban.webapp.AzkabanWebServer;
public class ProjectManagerServlet extends LoginAbstractAzkabanServlet {
private static final String APPLICATION_ZIP_MIME_TYPE = "application/zip";
private static final long serialVersionUID = 1;
private static final Logger logger = Logger
.getLogger(ProjectManagerServlet.class);
private static final NodeLevelComparator NODE_LEVEL_COMPARATOR =
new NodeLevelComparator();
private static final String LOCKDOWN_CREATE_PROJECTS_KEY =
"lockdown.create.projects";
private static final String LOCKDOWN_UPLOAD_PROJECTS_KEY =
"lockdown.upload.projects";
private static final String PROJECT_DOWNLOAD_BUFFER_SIZE_IN_BYTES =
"project.download.buffer.size";
private ProjectManager projectManager;
private ExecutorManagerAdapter executorManager;
private ScheduleManager scheduleManager;
private UserManager userManager;
private int downloadBufferSize;
private boolean lockdownCreateProjects = false;
private boolean lockdownUploadProjects = false;
private static Comparator<Flow> FLOW_ID_COMPARATOR = new Comparator<Flow>() {
@Override
public int compare(Flow f1, Flow f2) {
return f1.getId().compareTo(f2.getId());
}
};
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
AzkabanWebServer server = (AzkabanWebServer) getApplication();
projectManager = server.getProjectManager();
executorManager = server.getExecutorManager();
scheduleManager = server.getScheduleManager();
userManager = server.getUserManager();
lockdownCreateProjects =
server.getServerProps().getBoolean(LOCKDOWN_CREATE_PROJECTS_KEY, false);
if (lockdownCreateProjects) {
logger.info("Creation of projects is locked down");
}
lockdownUploadProjects =
server.getServerProps().getBoolean(LOCKDOWN_UPLOAD_PROJECTS_KEY, false);
if (lockdownUploadProjects) {
logger.info("Uploading of projects is locked down");
}
downloadBufferSize =
server.getServerProps().getInt(PROJECT_DOWNLOAD_BUFFER_SIZE_IN_BYTES,
8192);
logger.info("downloadBufferSize: " + downloadBufferSize);
}
@Override
protected void handleGet(HttpServletRequest req, HttpServletResponse resp,
Session session) throws ServletException, IOException {
if (hasParam(req, "project")) {
if (hasParam(req, "ajax")) {
handleAJAXAction(req, resp, session);
} else if (hasParam(req, "logs")) {
handleProjectLogsPage(req, resp, session);
} else if (hasParam(req, "permissions")) {
handlePermissionPage(req, resp, session);
} else if (hasParam(req, "prop")) {
handlePropertyPage(req, resp, session);
} else if (hasParam(req, "history")) {
handleJobHistoryPage(req, resp, session);
} else if (hasParam(req, "job")) {
handleJobPage(req, resp, session);
} else if (hasParam(req, "flow")) {
handleFlowPage(req, resp, session);
} else if (hasParam(req, "delete")) {
handleRemoveProject(req, resp, session);
} else if (hasParam(req, "purge")) {
handlePurgeProject(req, resp, session);
} else if (hasParam(req, "download")) {
handleDownloadProject(req, resp, session);
} else {
handleProjectPage(req, resp, session);
}
return;
} else if (hasParam(req, "reloadProjectWhitelist")) {
handleReloadProjectWhitelist(req, resp, session);
}
Page page =
newPage(req, resp, session,
"azkaban/webapp/servlet/velocity/projectpage.vm");
page.add("errorMsg", "No project set.");
page.render();
}
@Override
protected void handleMultiformPost(HttpServletRequest req,
HttpServletResponse resp, Map<String, Object> params, Session session)
throws ServletException, IOException {
// Looks like a duplicate, but this is a move away from the regular
// multiform post + redirect
// to a more ajax like command.
if (params.containsKey("ajax")) {
String action = (String) params.get("ajax");
HashMap<String, String> ret = new HashMap<String, String>();
if (action.equals("upload")) {
ajaxHandleUpload(req, resp, ret, params, session);
}
this.writeJSON(resp, ret);
} else if (params.containsKey("action")) {
String action = (String) params.get("action");
if (action.equals("upload")) {
handleUpload(req, resp, params, session);
}
}
}
@Override
protected void handlePost(HttpServletRequest req, HttpServletResponse resp,
Session session) throws ServletException, IOException {
if (hasParam(req, "action")) {
String action = getParam(req, "action");
if (action.equals("create")) {
handleCreate(req, resp, session);
}
}
}
private void handleAJAXAction(HttpServletRequest req,
HttpServletResponse resp, Session session) throws ServletException,
IOException {
String projectName = getParam(req, "project");
User user = session.getUser();
HashMap<String, Object> ret = new HashMap<>();
ret.put("project", projectName);
Project project = projectManager.getProject(projectName);
if (project == null) {
ret.put("error", "Project " + projectName + " doesn't exist.");
} else {
ret.put("projectId", project.getId());
String ajaxName = getParam(req, "ajax");
if (ajaxName.equals("getProjectId")) {
// Do nothing, since projectId is added to all AJAX requests.
} else if (ajaxName.equals("fetchProjectLogs")) {
if (handleAjaxPermission(project, user, Type.READ, ret)) {
ajaxFetchProjectLogEvents(project, req, ret);
}
} else if (ajaxName.equals("fetchflowjobs")) {
if (handleAjaxPermission(project, user, Type.READ, ret)) {
ajaxFetchFlow(project, ret, req);
}
} else if (ajaxName.equals("fetchflowdetails")) {
if (handleAjaxPermission(project, user, Type.READ, ret)) {
ajaxFetchFlowDetails(project, ret, req);
}
} else if (ajaxName.equals("fetchflowgraph")) {
if (handleAjaxPermission(project, user, Type.READ, ret)) {
ajaxFetchFlowGraph(project, ret, req);
}
} else if (ajaxName.equals("fetchflownodedata")) {
if (handleAjaxPermission(project, user, Type.READ, ret)) {
ajaxFetchFlowNodeData(project, ret, req);
}
} else if (ajaxName.equals("fetchprojectflows")) {
if (handleAjaxPermission(project, user, Type.READ, ret)) {
ajaxFetchProjectFlows(project, ret, req);
}
} else if (ajaxName.equals("changeDescription")) {
if (handleAjaxPermission(project, user, Type.WRITE, ret)) {
ajaxChangeDescription(project, ret, req, user);
}
} else if (ajaxName.equals("getPermissions")) {
if (handleAjaxPermission(project, user, Type.READ, ret)) {
ajaxGetPermissions(project, ret);
}
} else if (ajaxName.equals("getGroupPermissions")) {
if (handleAjaxPermission(project, user, Type.READ, ret)) {
ajaxGetGroupPermissions(project, ret);
}
} else if (ajaxName.equals("getProxyUsers")) {
if (handleAjaxPermission(project, user, Type.READ, ret)) {
ajaxGetProxyUsers(project, ret);
}
} else if (ajaxName.equals("changePermission")) {
if (handleAjaxPermission(project, user, Type.ADMIN, ret)) {
ajaxChangePermissions(project, ret, req, user);
}
} else if (ajaxName.equals("addPermission")) {
if (handleAjaxPermission(project, user, Type.ADMIN, ret)) {
ajaxAddPermission(project, ret, req, user);
}
} else if (ajaxName.equals("addProxyUser")) {
if (handleAjaxPermission(project, user, Type.ADMIN, ret)) {
ajaxAddProxyUser(project, ret, req, user);
}
} else if (ajaxName.equals("removeProxyUser")) {
if (handleAjaxPermission(project, user, Type.ADMIN, ret)) {
ajaxRemoveProxyUser(project, ret, req, user);
}
} else if (ajaxName.equals("fetchFlowExecutions")) {
if (handleAjaxPermission(project, user, Type.READ, ret)) {
ajaxFetchFlowExecutions(project, ret, req);
}
} else if (ajaxName.equals("fetchLastSuccessfulFlowExecution")) {
if (handleAjaxPermission(project, user, Type.READ, ret)) {
ajaxFetchLastSuccessfulFlowExecution(project, ret, req);
}
} else if (ajaxName.equals("fetchJobInfo")) {
if (handleAjaxPermission(project, user, Type.READ, ret)) {
ajaxFetchJobInfo(project, ret, req);
}
} else if (ajaxName.equals("setJobOverrideProperty")) {
if (handleAjaxPermission(project, user, Type.WRITE, ret)) {
ajaxSetJobOverrideProperty(project, ret, req, user);
}
} else {
ret.put("error", "Cannot execute command " + ajaxName);
}
}
this.writeJSON(resp, ret);
}
private boolean handleAjaxPermission(Project project, User user, Type type,
Map<String, Object> ret) {
if (hasPermission(project, user, type)) {
return true;
}
ret.put("error", "Permission denied. Need " + type.toString() + " access.");
return false;
}
private void ajaxFetchProjectLogEvents(Project project,
HttpServletRequest req, HashMap<String, Object> ret) throws ServletException {
int num = this.getIntParam(req, "size", 1000);
int skip = this.getIntParam(req, "skip", 0);
List<ProjectLogEvent> logEvents;
try {
logEvents = projectManager.getProjectEventLogs(project, num, skip);
} catch (ProjectManagerException e) {
throw new ServletException(e);
}
String[] columns = new String[] { "user", "time", "type", "message" };
ret.put("columns", columns);
List<Object[]> eventData = new ArrayList<>();
for (ProjectLogEvent events : logEvents) {
Object[] entry = new Object[4];
entry[0] = events.getUser();
entry[1] = events.getTime();
entry[2] = events.getType();
entry[3] = events.getMessage();
eventData.add(entry);
}
ret.put("logData", eventData);
}
private List<String> getFlowJobTypes(Flow flow) {
Set<String> jobTypeSet = new HashSet<String>();
for (Node node : flow.getNodes()) {
jobTypeSet.add(node.getType());
}
List<String> jobTypes = new ArrayList<String>();
jobTypes.addAll(jobTypeSet);
return jobTypes;
}
private void ajaxFetchFlowDetails(Project project,
HashMap<String, Object> ret, HttpServletRequest req)
throws ServletException {
String flowName = getParam(req, "flow");
Flow flow = null;
try {
flow = project.getFlow(flowName);
if (flow == null) {
ret.put("error", "Flow " + flowName + " not found.");
return;
}
ret.put("jobTypes", getFlowJobTypes(flow));
} catch (AccessControlException e) {
ret.put("error", e.getMessage());
}
}
private void ajaxFetchLastSuccessfulFlowExecution(Project project,
HashMap<String, Object> ret, HttpServletRequest req)
throws ServletException {
String flowId = getParam(req, "flow");
List<ExecutableFlow> exFlows = null;
try {
exFlows =
executorManager.getExecutableFlows(project.getId(), flowId, 0, 1,
Status.SUCCEEDED);
} catch (ExecutorManagerException e) {
ret.put("error", "Error retrieving executable flows");
return;
}
if (exFlows.size() == 0) {
ret.put("success", "false");
ret.put("message", "This flow has no successful run.");
return;
}
ret.put("success", "true");
ret.put("message", "");
ret.put("execId", exFlows.get(0).getExecutionId());
}
private void ajaxFetchFlowExecutions(Project project,
HashMap<String, Object> ret, HttpServletRequest req)
throws ServletException {
String flowId = getParam(req, "flow");
int from = Integer.valueOf(getParam(req, "start"));
int length = Integer.valueOf(getParam(req, "length"));
ArrayList<ExecutableFlow> exFlows = new ArrayList<ExecutableFlow>();
int total = 0;
try {
total =
executorManager.getExecutableFlows(project.getId(), flowId, from,
length, exFlows);
} catch (ExecutorManagerException e) {
ret.put("error", "Error retrieving executable flows");
}
ret.put("flow", flowId);
ret.put("total", total);
ret.put("from", from);
ret.put("length", length);
ArrayList<Object> history = new ArrayList<Object>();
for (ExecutableFlow flow : exFlows) {
HashMap<String, Object> flowInfo = new HashMap<String, Object>();
flowInfo.put("execId", flow.getExecutionId());
flowInfo.put("flowId", flow.getFlowId());
flowInfo.put("projectId", flow.getProjectId());
flowInfo.put("status", flow.getStatus().toString());
flowInfo.put("submitTime", flow.getSubmitTime());
flowInfo.put("startTime", flow.getStartTime());
flowInfo.put("endTime", flow.getEndTime());
flowInfo.put("submitUser", flow.getSubmitUser());
history.add(flowInfo);
}
ret.put("executions", history);
}
/**
* Download project zip file from DB and send it back client.
*
* This method requires a project name and an optional project version.
*
* @param req
* @param resp
* @param session
* @throws ServletException
* @throws IOException
*/
private void handleDownloadProject(HttpServletRequest req,
HttpServletResponse resp, Session session) throws ServletException,
IOException {
User user = session.getUser();
String projectName = getParam(req, "project");
logger.info(user.getUserId() + " is downloading project: " + projectName);
Project project = projectManager.getProject(projectName);
if (project == null) {
this.setErrorMessageInCookie(resp, "Project " + projectName
+ " doesn't exist.");
resp.sendRedirect(req.getContextPath());
return;
}
int version = -1;
if (hasParam(req, "version")) {
version = getIntParam(req, "version");
}
ProjectFileHandler projectFileHandler = null;
FileInputStream inStream = null;
OutputStream outStream = null;
try {
projectFileHandler =
projectManager.getProjectFileHandler(project, version);
if (projectFileHandler == null) {
this.setErrorMessageInCookie(resp, "Project " + projectName
+ " with version " + version + " doesn't exist");
resp.sendRedirect(req.getContextPath());
return;
}
File projectZipFile = projectFileHandler.getLocalFile();
String logStr =
String.format(
"downloading project zip file for project \"%s\" at \"%s\""
+ " size: %d type: %s fileName: \"%s\"",
projectFileHandler.getFileName(),
projectZipFile.getAbsolutePath(), projectZipFile.length(),
projectFileHandler.getFileType(),
projectFileHandler.getFileName());
logger.info(logStr);
// now set up HTTP response for downloading file
inStream = new FileInputStream(projectZipFile);
resp.setContentType(APPLICATION_ZIP_MIME_TYPE);
String headerKey = "Content-Disposition";
String headerValue =
String.format("attachment; filename=\"%s\"",
projectFileHandler.getFileName());
resp.setHeader(headerKey, headerValue);
resp.setHeader("version",
Integer.toString(projectFileHandler.getVersion()));
resp.setHeader("projectId",
Integer.toString(projectFileHandler.getProjectId()));
outStream = resp.getOutputStream();
byte[] buffer = new byte[downloadBufferSize];
int bytesRead = -1;
while ((bytesRead = inStream.read(buffer)) != -1) {
outStream.write(buffer, 0, bytesRead);
}
} catch (Throwable e) {
logger.error(
"Encountered error while downloading project zip file for project: "
+ projectName + " by user: " + user.getUserId(), e);
throw new ServletException(e);
} finally {
IOUtils.closeQuietly(inStream);
IOUtils.closeQuietly(outStream);
if (projectFileHandler != null) {
projectFileHandler.deleteLocalFile();
}
}
}
/**
* validate readiness of a project and user permission and use projectManager
* to purge the project if things looks good
**/
private void handlePurgeProject(HttpServletRequest req,
HttpServletResponse resp, Session session) throws ServletException,
IOException {
User user = session.getUser();
HashMap<String, Object> ret = new HashMap<String, Object>();
boolean isOperationSuccessful = true;
try {
Project project = null;
String projectParam = getParam(req, "project");
if (StringUtils.isNumeric(projectParam)) {
project = projectManager.getProject(Integer.parseInt(projectParam)); // get
// project
// by
// Id
} else {
project = projectManager.getProject(projectParam); // get project by
// name (name cannot
// start
// from ints)
}
// invalid project
if (project == null) {
ret.put("error", "invalid project");
isOperationSuccessful = false;
}
// project is already deleted
if (isOperationSuccessful
&& projectManager.isActiveProject(project.getId())) {
ret.put("error", "Project " + project.getName()
+ " should be deleted before purging");
isOperationSuccessful = false;
}
// only eligible users can purge a project
if (isOperationSuccessful && !hasPermission(project, user, Type.ADMIN)) {
ret.put("error", "Cannot purge. User '" + user.getUserId()
+ "' is not an ADMIN.");
isOperationSuccessful = false;
}
if (isOperationSuccessful) {
projectManager.purgeProject(project, user);
}
} catch (Exception e) {
ret.put("error", e.getMessage());
isOperationSuccessful = false;
}
ret.put("success", isOperationSuccessful);
this.writeJSON(resp, ret);
}
private void handleRemoveProject(HttpServletRequest req,
HttpServletResponse resp, Session session) throws ServletException,
IOException {
User user = session.getUser();
String projectName = getParam(req, "project");
Project project = projectManager.getProject(projectName);
if (project == null) {
this.setErrorMessageInCookie(resp, "Project " + projectName
+ " doesn't exist.");
resp.sendRedirect(req.getContextPath());
return;
}
if (!hasPermission(project, user, Type.ADMIN)) {
this.setErrorMessageInCookie(resp,
"Cannot delete. User '" + user.getUserId() + "' is not an ADMIN.");
resp.sendRedirect(req.getRequestURI() + "?project=" + projectName);
return;
}
// Check if scheduled
Schedule sflow = null;
try {
for (Schedule flow : scheduleManager.getSchedules()) {
if (flow.getProjectId() == project.getId()) {
sflow = flow;
break;
}
}
} catch (ScheduleManagerException e) {
throw new ServletException(e);
}
if (sflow != null) {
this.setErrorMessageInCookie(resp, "Cannot delete. Please unschedule "
+ sflow.getScheduleName() + ".");
resp.sendRedirect(req.getRequestURI() + "?project=" + projectName);
return;
}
// Check if executing
ExecutableFlow exflow = null;
for (ExecutableFlow flow : executorManager.getRunningFlows()) {
if (flow.getProjectId() == project.getId()) {
exflow = flow;
break;
}
}
if (exflow != null) {
this.setErrorMessageInCookie(resp, "Cannot delete. Executable flow "
+ exflow.getExecutionId() + " is still running.");
resp.sendRedirect(req.getRequestURI() + "?project=" + projectName);
return;
}
try {
projectManager.removeProject(project, user);
} catch (ProjectManagerException e) {
this.setErrorMessageInCookie(resp, e.getMessage());
resp.sendRedirect(req.getRequestURI() + "?project=" + projectName);
return;
}
this.setSuccessMessageInCookie(resp, "Project '" + projectName
+ "' was successfully deleted.");
resp.sendRedirect(req.getContextPath());
}
private void ajaxChangeDescription(Project project,
HashMap<String, Object> ret, HttpServletRequest req, User user)
throws ServletException {
String description = getParam(req, "description");
project.setDescription(description);
try {
projectManager.updateProjectDescription(project, description, user);
} catch (ProjectManagerException e) {
ret.put("error", e.getMessage());
}
}
private void ajaxFetchJobInfo(Project project, HashMap<String, Object> ret,
HttpServletRequest req) throws ServletException {
String flowName = getParam(req, "flowName");
String jobName = getParam(req, "jobName");
Flow flow = project.getFlow(flowName);
if (flow == null) {
ret.put("error",
"Flow " + flowName + " not found in project " + project.getName());
return;
}
Node node = flow.getNode(jobName);
if (node == null) {
ret.put("error", "Job " + jobName + " not found in flow " + flowName);
return;
}
Props prop;
try {
prop = projectManager.getProperties(project, node.getJobSource());
} catch (ProjectManagerException e) {
ret.put("error", "Failed to retrieve job properties!");
return;
}
Props overrideProp;
try {
overrideProp = projectManager.getJobOverrideProperty(project, jobName);
} catch (ProjectManagerException e) {
ret.put("error", "Failed to retrieve job override properties!");
return;
}
ret.put("jobName", node.getId());
ret.put("jobType", prop.get("type"));
if (overrideProp == null) {
overrideProp = new Props(prop);
}
Map<String, String> generalParams = new HashMap<String, String>();
Map<String, String> overrideParams = new HashMap<String, String>();
for (String ps : prop.getKeySet()) {
generalParams.put(ps, prop.getString(ps));
}
for (String ops : overrideProp.getKeySet()) {
overrideParams.put(ops, overrideProp.getString(ops));
}
ret.put("generalParams", generalParams);
ret.put("overrideParams", overrideParams);
}
private void ajaxSetJobOverrideProperty(Project project,
HashMap<String, Object> ret, HttpServletRequest req, User user)
throws ServletException {
String flowName = getParam(req, "flowName");
String jobName = getParam(req, "jobName");
Flow flow = project.getFlow(flowName);
if (flow == null) {
ret.put("error",
"Flow " + flowName + " not found in project " + project.getName());
return;
}
Node node = flow.getNode(jobName);
if (node == null) {
ret.put("error", "Job " + jobName + " not found in flow " + flowName);
return;
}
Map<String, String> jobParamGroup = this.getParamGroup(req, "jobOverride");
@SuppressWarnings("unchecked")
Props overrideParams = new Props(null, jobParamGroup);
try {
projectManager.setJobOverrideProperty(project, overrideParams, jobName, user);
} catch (ProjectManagerException e) {
ret.put("error", "Failed to upload job override property");
}
}
private void ajaxFetchProjectFlows(Project project,
HashMap<String, Object> ret, HttpServletRequest req)
throws ServletException {
ArrayList<Map<String, Object>> flowList =
new ArrayList<Map<String, Object>>();
for (Flow flow : project.getFlows()) {
HashMap<String, Object> flowObj = new HashMap<String, Object>();
flowObj.put("flowId", flow.getId());
flowList.add(flowObj);
}
ret.put("flows", flowList);
}
private void ajaxFetchFlowGraph(Project project, HashMap<String, Object> ret,
HttpServletRequest req) throws ServletException {
String flowId = getParam(req, "flow");
fillFlowInfo(project, flowId, ret);
}
private void fillFlowInfo(Project project, String flowId,
HashMap<String, Object> ret) {
Flow flow = project.getFlow(flowId);
ArrayList<Map<String, Object>> nodeList =
new ArrayList<Map<String, Object>>();
for (Node node : flow.getNodes()) {
HashMap<String, Object> nodeObj = new HashMap<String, Object>();
nodeObj.put("id", node.getId());
nodeObj.put("type", node.getType());
if (node.getEmbeddedFlowId() != null) {
nodeObj.put("flowId", node.getEmbeddedFlowId());
fillFlowInfo(project, node.getEmbeddedFlowId(), nodeObj);
}
nodeList.add(nodeObj);
Set<Edge> inEdges = flow.getInEdges(node.getId());
if (inEdges != null && !inEdges.isEmpty()) {
ArrayList<String> inEdgesList = new ArrayList<String>();
for (Edge edge : inEdges) {
inEdgesList.add(edge.getSourceId());
}
Collections.sort(inEdgesList);
nodeObj.put("in", inEdgesList);
}
}
Collections.sort(nodeList, new Comparator<Map<String, Object>>() {
@Override
public int compare(Map<String, Object> o1, Map<String, Object> o2) {
String id = (String) o1.get("id");
return id.compareTo((String) o2.get("id"));
}
});
ret.put("flow", flowId);
ret.put("nodes", nodeList);
}
private void ajaxFetchFlowNodeData(Project project,
HashMap<String, Object> ret, HttpServletRequest req)
throws ServletException {
String flowId = getParam(req, "flow");
Flow flow = project.getFlow(flowId);
String nodeId = getParam(req, "node");
Node node = flow.getNode(nodeId);
if (node == null) {
ret.put("error", "Job " + nodeId + " doesn't exist.");
return;
}
ret.put("id", nodeId);
ret.put("flow", flowId);
ret.put("type", node.getType());
Props props;
try {
props = projectManager.getProperties(project, node.getJobSource());
} catch (ProjectManagerException e) {
ret.put("error", "Failed to upload job override property for " + nodeId);
return;
}
if (props == null) {
ret.put("error", "Properties for " + nodeId + " isn't found.");
return;
}
Map<String, String> properties = PropsUtils.toStringMap(props, true);
ret.put("props", properties);
if (node.getType().equals("flow")) {
if (node.getEmbeddedFlowId() != null) {
fillFlowInfo(project, node.getEmbeddedFlowId(), ret);
}
}
}
private void ajaxFetchFlow(Project project, HashMap<String, Object> ret,
HttpServletRequest req) throws ServletException {
String flowId = getParam(req, "flow");
Flow flow = project.getFlow(flowId);
ArrayList<Node> flowNodes = new ArrayList<Node>(flow.getNodes());
Collections.sort(flowNodes, NODE_LEVEL_COMPARATOR);
ArrayList<Object> nodeList = new ArrayList<Object>();
for (Node node : flowNodes) {
HashMap<String, Object> nodeObj = new HashMap<String, Object>();
nodeObj.put("id", node.getId());
ArrayList<String> dependencies = new ArrayList<String>();
Collection<Edge> collection = flow.getInEdges(node.getId());
if (collection != null) {
for (Edge edge : collection) {
dependencies.add(edge.getSourceId());
}
}
ArrayList<String> dependents = new ArrayList<String>();
collection = flow.getOutEdges(node.getId());
if (collection != null) {
for (Edge edge : collection) {
dependents.add(edge.getTargetId());
}
}
nodeObj.put("dependencies", dependencies);
nodeObj.put("dependents", dependents);
nodeObj.put("level", node.getLevel());
nodeList.add(nodeObj);
}
ret.put("flowId", flowId);
ret.put("nodes", nodeList);
}
private void ajaxAddProxyUser(Project project, HashMap<String, Object> ret,
HttpServletRequest req, User user) throws ServletException {
String name = getParam(req, "name");
logger.info("Adding proxy user " + name + " by " + user.getUserId());
if (userManager.validateProxyUser(name, user)) {
try {
projectManager.addProjectProxyUser(project, name, user);
} catch (ProjectManagerException e) {
ret.put("error", e.getMessage());
}
} else {
ret.put("error", "User " + user.getUserId()
+ " has no permission to add " + name + " as proxy user.");
return;
}
}
private void ajaxRemoveProxyUser(Project project,
HashMap<String, Object> ret, HttpServletRequest req, User user)
throws ServletException {
String name = getParam(req, "name");
logger.info("Removing proxy user " + name + " by " + user.getUserId());
try {
projectManager.removeProjectProxyUser(project, name, user);
} catch (ProjectManagerException e) {
ret.put("error", e.getMessage());
}
}
private void ajaxAddPermission(Project project, HashMap<String, Object> ret,
HttpServletRequest req, User user) throws ServletException {
String name = getParam(req, "name");
boolean group = Boolean.parseBoolean(getParam(req, "group"));
if (group) {
if (project.getGroupPermission(name) != null) {
ret.put("error", "Group permission already exists.");
return;
}
if (!userManager.validateGroup(name)) {
ret.put("error", "Group is invalid.");
return;
}
} else {
if (project.getUserPermission(name) != null) {
ret.put("error", "User permission already exists.");
return;
}
if (!userManager.validateUser(name)) {
ret.put("error", "User is invalid.");
return;
}
}
boolean admin = Boolean.parseBoolean(getParam(req, "permissions[admin]"));
boolean read = Boolean.parseBoolean(getParam(req, "permissions[read]"));
boolean write = Boolean.parseBoolean(getParam(req, "permissions[write]"));
boolean execute =
Boolean.parseBoolean(getParam(req, "permissions[execute]"));
boolean schedule =
Boolean.parseBoolean(getParam(req, "permissions[schedule]"));
Permission perm = new Permission();
if (admin) {
perm.setPermission(Type.ADMIN, true);
} else {
perm.setPermission(Type.READ, read);
perm.setPermission(Type.WRITE, write);
perm.setPermission(Type.EXECUTE, execute);
perm.setPermission(Type.SCHEDULE, schedule);
}
try {
projectManager.updateProjectPermission(project, name, perm, group, user);
} catch (ProjectManagerException e) {
ret.put("error", e.getMessage());
}
}
private void ajaxChangePermissions(Project project,
HashMap<String, Object> ret, HttpServletRequest req, User user)
throws ServletException {
boolean admin = Boolean.parseBoolean(getParam(req, "permissions[admin]"));
boolean read = Boolean.parseBoolean(getParam(req, "permissions[read]"));
boolean write = Boolean.parseBoolean(getParam(req, "permissions[write]"));
boolean execute =
Boolean.parseBoolean(getParam(req, "permissions[execute]"));
boolean schedule =
Boolean.parseBoolean(getParam(req, "permissions[schedule]"));
boolean group = Boolean.parseBoolean(getParam(req, "group"));
String name = getParam(req, "name");
Permission perm;
if (group) {
perm = project.getGroupPermission(name);
} else {
perm = project.getUserPermission(name);
}
if (perm == null) {
ret.put("error", "Permissions for " + name + " cannot be found.");
return;
}
if (admin || read || write || execute || schedule) {
if (admin) {
perm.setPermission(Type.ADMIN, true);
perm.setPermission(Type.READ, false);
perm.setPermission(Type.WRITE, false);
perm.setPermission(Type.EXECUTE, false);
perm.setPermission(Type.SCHEDULE, false);
} else {
perm.setPermission(Type.ADMIN, false);
perm.setPermission(Type.READ, read);
perm.setPermission(Type.WRITE, write);
perm.setPermission(Type.EXECUTE, execute);
perm.setPermission(Type.SCHEDULE, schedule);
}
try {
projectManager
.updateProjectPermission(project, name, perm, group, user);
} catch (ProjectManagerException e) {
ret.put("error", e.getMessage());
}
} else {
try {
projectManager.removeProjectPermission(project, name, group, user);
} catch (ProjectManagerException e) {
ret.put("error", e.getMessage());
}
}
}
/**
* this only returns user permissions, but not group permissions and proxy
* users
*
* @param project
* @param ret
*/
private void ajaxGetPermissions(Project project, HashMap<String, Object> ret) {
ArrayList<HashMap<String, Object>> permissions =
new ArrayList<HashMap<String, Object>>();
for (Pair<String, Permission> perm : project.getUserPermissions()) {
HashMap<String, Object> permObj = new HashMap<String, Object>();
String userId = perm.getFirst();
permObj.put("username", userId);
permObj.put("permission", perm.getSecond().toStringArray());
permissions.add(permObj);
}
ret.put("permissions", permissions);
}
private void ajaxGetGroupPermissions(Project project,
HashMap<String, Object> ret) {
ArrayList<HashMap<String, Object>> permissions =
new ArrayList<HashMap<String, Object>>();
for (Pair<String, Permission> perm : project.getGroupPermissions()) {
HashMap<String, Object> permObj = new HashMap<String, Object>();
String userId = perm.getFirst();
permObj.put("username", userId);
permObj.put("permission", perm.getSecond().toStringArray());
permissions.add(permObj);
}
ret.put("permissions", permissions);
}
private void ajaxGetProxyUsers(Project project, HashMap<String, Object> ret) {
String[] proxyUsers = project.getProxyUsers().toArray(new String[0]);
ret.put("proxyUsers", proxyUsers);
}
private void handleProjectLogsPage(HttpServletRequest req,
HttpServletResponse resp, Session session) throws ServletException,
IOException {
Page page =
newPage(req, resp, session,
"azkaban/webapp/servlet/velocity/projectlogpage.vm");
String projectName = getParam(req, "project");
User user = session.getUser();
Project project = null;
try {
project = projectManager.getProject(projectName);
if (project == null) {
page.add("errorMsg", "Project " + projectName + " doesn't exist.");
} else {
if (!hasPermission(project, user, Type.READ)) {
throw new AccessControlException("No permission to view project "
+ projectName + ".");
}
page.add("project", project);
page.add("admins", Utils.flattenToString(
project.getUsersWithPermission(Type.ADMIN), ","));
Permission perm = this.getPermissionObject(project, user, Type.ADMIN);
page.add("userpermission", perm);
boolean adminPerm = perm.isPermissionSet(Type.ADMIN);
if (adminPerm) {
page.add("admin", true);
}
// Set this so we can display execute buttons only to those who have
// access.
if (perm.isPermissionSet(Type.EXECUTE) || adminPerm) {
page.add("exec", true);
} else {
page.add("exec", false);
}
}
} catch (AccessControlException e) {
page.add("errorMsg", e.getMessage());
}
int numBytes = 1024;
// Really sucks if we do a lot of these because it'll eat up memory fast.
// But it's expected that this won't be a heavily used thing. If it is,
// then we'll revisit it to make it more stream friendly.
StringBuffer buffer = new StringBuffer(numBytes);
page.add("log", buffer.toString());
page.render();
}
private void handleJobHistoryPage(HttpServletRequest req,
HttpServletResponse resp, Session session) throws ServletException,
IOException {
Page page =
newPage(req, resp, session,
"azkaban/webapp/servlet/velocity/jobhistorypage.vm");
String projectName = getParam(req, "project");
User user = session.getUser();
Project project = projectManager.getProject(projectName);
if (project == null) {
page.add("errorMsg", "Project " + projectName + " doesn't exist.");
page.render();
return;
}
if (!hasPermission(project, user, Type.READ)) {
page.add("errorMsg", "No permission to view project " + projectName + ".");
page.render();
return;
}
String jobId = getParam(req, "job");
int pageNum = getIntParam(req, "page", 1);
int pageSize = getIntParam(req, "size", 25);
page.add("projectId", project.getId());
page.add("projectName", project.getName());
page.add("jobid", jobId);
page.add("page", pageNum);
int skipPage = (pageNum - 1) * pageSize;
int numResults = 0;
try {
numResults = executorManager.getNumberOfJobExecutions(project, jobId);
int maxPage = (numResults / pageSize) + 1;
List<ExecutableJobInfo> jobInfo =
executorManager.getExecutableJobs(project, jobId, skipPage, pageSize);
if (jobInfo == null || jobInfo.isEmpty()) {
jobInfo = null;
}
page.add("history", jobInfo);
page.add("previous", new PageSelection("Previous", pageSize, true, false,
Math.max(pageNum - 1, 1)));
page.add(
"next",
new PageSelection("Next", pageSize, false, false, Math.min(
pageNum + 1, maxPage)));
if (jobInfo != null) {
ArrayList<Object> dataSeries = new ArrayList<Object>();
for (ExecutableJobInfo info : jobInfo) {
Map<String, Object> map = info.toObject();
dataSeries.add(map);
}
page.add("dataSeries", JSONUtils.toJSON(dataSeries));
} else {
page.add("dataSeries", "[]");
}
} catch (ExecutorManagerException e) {
page.add("errorMsg", e.getMessage());
}
// Now for the 5 other values.
int pageStartValue = 1;
if (pageNum > 3) {
pageStartValue = pageNum - 2;
}
int maxPage = (numResults / pageSize) + 1;
page.add(
"page1",
new PageSelection(String.valueOf(pageStartValue), pageSize,
pageStartValue > maxPage, pageStartValue == pageNum, Math.min(
pageStartValue, maxPage)));
pageStartValue++;
page.add(
"page2",
new PageSelection(String.valueOf(pageStartValue), pageSize,
pageStartValue > maxPage, pageStartValue == pageNum, Math.min(
pageStartValue, maxPage)));
pageStartValue++;
page.add(
"page3",
new PageSelection(String.valueOf(pageStartValue), pageSize,
pageStartValue > maxPage, pageStartValue == pageNum, Math.min(
pageStartValue, maxPage)));
pageStartValue++;
page.add(
"page4",
new PageSelection(String.valueOf(pageStartValue), pageSize,
pageStartValue > maxPage, pageStartValue == pageNum, Math.min(
pageStartValue, maxPage)));
pageStartValue++;
page.add(
"page5",
new PageSelection(String.valueOf(pageStartValue), pageSize,
pageStartValue > maxPage, pageStartValue == pageNum, Math.min(
pageStartValue, maxPage)));
page.render();
}
private void handlePermissionPage(HttpServletRequest req,
HttpServletResponse resp, Session session) throws ServletException {
Page page =
newPage(req, resp, session,
"azkaban/webapp/servlet/velocity/permissionspage.vm");
String projectName = getParam(req, "project");
User user = session.getUser();
Project project = null;
try {
project = projectManager.getProject(projectName);
if (project == null) {
page.add("errorMsg", "Project " + projectName + " not found.");
} else {
if (!hasPermission(project, user, Type.READ)) {
throw new AccessControlException("No permission to view project "
+ projectName + ".");
}
page.add("project", project);
page.add("username", user.getUserId());
page.add("admins", Utils.flattenToString(
project.getUsersWithPermission(Type.ADMIN), ","));
Permission perm = this.getPermissionObject(project, user, Type.ADMIN);
page.add("userpermission", perm);
if (perm.isPermissionSet(Type.ADMIN)) {
page.add("admin", true);
}
List<Pair<String, Permission>> userPermission =
project.getUserPermissions();
if (userPermission != null && !userPermission.isEmpty()) {
page.add("permissions", userPermission);
}
List<Pair<String, Permission>> groupPermission =
project.getGroupPermissions();
if (groupPermission != null && !groupPermission.isEmpty()) {
page.add("groupPermissions", groupPermission);
}
Set<String> proxyUsers = project.getProxyUsers();
if (proxyUsers != null && !proxyUsers.isEmpty()) {
page.add("proxyUsers", proxyUsers);
}
if (hasPermission(project, user, Type.ADMIN)) {
page.add("isAdmin", true);
}
}
} catch (AccessControlException e) {
page.add("errorMsg", e.getMessage());
}
page.render();
}
private void handleJobPage(HttpServletRequest req, HttpServletResponse resp,
Session session) throws ServletException {
Page page =
newPage(req, resp, session,
"azkaban/webapp/servlet/velocity/jobpage.vm");
String projectName = getParam(req, "project");
String flowName = getParam(req, "flow");
String jobName = getParam(req, "job");
User user = session.getUser();
Project project = null;
Flow flow = null;
try {
project = projectManager.getProject(projectName);
if (project == null) {
page.add("errorMsg", "Project " + projectName + " not found.");
page.render();
return;
}
if (!hasPermission(project, user, Type.READ)) {
throw new AccessControlException("No permission to view project "
+ projectName + ".");
}
page.add("project", project);
flow = project.getFlow(flowName);
if (flow == null) {
page.add("errorMsg", "Flow " + flowName + " not found.");
page.render();
return;
}
page.add("flowid", flow.getId());
Node node = flow.getNode(jobName);
if (node == null) {
page.add("errorMsg", "Job " + jobName + " not found.");
page.render();
return;
}
Props prop = projectManager.getProperties(project, node.getJobSource());
Props overrideProp =
projectManager.getJobOverrideProperty(project, jobName);
if (overrideProp == null) {
overrideProp = new Props();
}
Props comboProp = new Props(prop);
for (String key : overrideProp.getKeySet()) {
comboProp.put(key, overrideProp.get(key));
}
page.add("jobid", node.getId());
page.add("jobtype", node.getType());
ArrayList<String> dependencies = new ArrayList<String>();
Set<Edge> inEdges = flow.getInEdges(node.getId());
if (inEdges != null) {
for (Edge dependency : inEdges) {
dependencies.add(dependency.getSourceId());
}
}
if (!dependencies.isEmpty()) {
page.add("dependencies", dependencies);
}
ArrayList<String> dependents = new ArrayList<String>();
Set<Edge> outEdges = flow.getOutEdges(node.getId());
if (outEdges != null) {
for (Edge dependent : outEdges) {
dependents.add(dependent.getTargetId());
}
}
if (!dependents.isEmpty()) {
page.add("dependents", dependents);
}
// Resolve property dependencies
ArrayList<String> source = new ArrayList<String>();
String nodeSource = node.getPropsSource();
if (nodeSource != null) {
source.add(nodeSource);
FlowProps parent = flow.getFlowProps(nodeSource);
while (parent.getInheritedSource() != null) {
source.add(parent.getInheritedSource());
parent = flow.getFlowProps(parent.getInheritedSource());
}
}
if (!source.isEmpty()) {
page.add("properties", source);
}
ArrayList<Pair<String, String>> parameters =
new ArrayList<Pair<String, String>>();
// Parameter
for (String key : comboProp.getKeySet()) {
String value = comboProp.get(key);
parameters.add(new Pair<String, String>(key, value));
}
page.add("parameters", parameters);
} catch (AccessControlException e) {
page.add("errorMsg", e.getMessage());
} catch (ProjectManagerException e) {
page.add("errorMsg", e.getMessage());
}
page.render();
}
private void handlePropertyPage(HttpServletRequest req,
HttpServletResponse resp, Session session) throws ServletException {
Page page =
newPage(req, resp, session,
"azkaban/webapp/servlet/velocity/propertypage.vm");
String projectName = getParam(req, "project");
String flowName = getParam(req, "flow");
String jobName = getParam(req, "job");
String propSource = getParam(req, "prop");
User user = session.getUser();
Project project = null;
Flow flow = null;
try {
project = projectManager.getProject(projectName);
if (project == null) {
page.add("errorMsg", "Project " + projectName + " not found.");
logger.info("Display project property. Project " + projectName + " not found.");
page.render();
return;
}
if (!hasPermission(project, user, Type.READ)) {
throw new AccessControlException("No permission to view project "
+ projectName + ".");
}
page.add("project", project);
flow = project.getFlow(flowName);
if (flow == null) {
page.add("errorMsg", "Flow " + flowName + " not found.");
logger.info("Display project property. Project " + projectName +
" Flow " + flowName + " not found.");
page.render();
return;
}
page.add("flowid", flow.getId());
Node node = flow.getNode(jobName);
if (node == null) {
page.add("errorMsg", "Job " + jobName + " not found.");
logger.info("Display project property. Project " + projectName +
" Flow " + flowName + " Job " + jobName + " not found.");
page.render();
return;
}
Props prop = projectManager.getProperties(project, propSource);
if (prop == null) {
page.add("errorMsg", "Property " + propSource + " not found.");
logger.info("Display project property. Project " + projectName +
" Flow " + flowName + " Job " + jobName +
" Property " + propSource + " not found.");
page.render();
return;
}
page.add("property", propSource);
page.add("jobid", node.getId());
// Resolve property dependencies
ArrayList<String> inheritProps = new ArrayList<String>();
FlowProps parent = flow.getFlowProps(propSource);
while (parent.getInheritedSource() != null) {
inheritProps.add(parent.getInheritedSource());
parent = flow.getFlowProps(parent.getInheritedSource());
}
if (!inheritProps.isEmpty()) {
page.add("inheritedproperties", inheritProps);
}
ArrayList<String> dependingProps = new ArrayList<String>();
FlowProps child =
flow.getFlowProps(flow.getNode(jobName).getPropsSource());
while (!child.getSource().equals(propSource)) {
dependingProps.add(child.getSource());
child = flow.getFlowProps(child.getInheritedSource());
}
if (!dependingProps.isEmpty()) {
page.add("dependingproperties", dependingProps);
}
ArrayList<Pair<String, String>> parameters =
new ArrayList<Pair<String, String>>();
// Parameter
for (String key : prop.getKeySet()) {
String value = prop.get(key);
parameters.add(new Pair<String, String>(key, value));
}
page.add("parameters", parameters);
} catch (AccessControlException e) {
page.add("errorMsg", e.getMessage());
} catch (ProjectManagerException e) {
page.add("errorMsg", e.getMessage());
}
page.render();
}
private void handleFlowPage(HttpServletRequest req, HttpServletResponse resp,
Session session) throws ServletException {
Page page =
newPage(req, resp, session,
"azkaban/webapp/servlet/velocity/flowpage.vm");
String projectName = getParam(req, "project");
String flowName = getParam(req, "flow");
User user = session.getUser();
Project project = null;
Flow flow = null;
try {
project = projectManager.getProject(projectName);
if (project == null) {
page.add("errorMsg", "Project " + projectName + " not found.");
page.render();
return;
}
if (!hasPermission(project, user, Type.READ)) {
throw new AccessControlException("No permission Project " + projectName
+ ".");
}
page.add("project", project);
flow = project.getFlow(flowName);
if (flow == null) {
page.add("errorMsg", "Flow " + flowName + " not found.");
} else {
page.add("flowid", flow.getId());
}
} catch (AccessControlException e) {
page.add("errorMsg", e.getMessage());
}
page.render();
}
private void handleProjectPage(HttpServletRequest req,
HttpServletResponse resp, Session session) throws ServletException {
Page page =
newPage(req, resp, session,
"azkaban/webapp/servlet/velocity/projectpage.vm");
String projectName = getParam(req, "project");
User user = session.getUser();
Project project = null;
try {
project = projectManager.getProject(projectName);
if (project == null) {
page.add("errorMsg", "Project " + projectName + " not found.");
} else {
if (!hasPermission(project, user, Type.READ)) {
throw new AccessControlException("No permission to view project "
+ projectName + ".");
}
page.add("project", project);
page.add("admins", Utils.flattenToString(
project.getUsersWithPermission(Type.ADMIN), ","));
Permission perm = this.getPermissionObject(project, user, Type.ADMIN);
page.add("userpermission", perm);
page.add(
"validatorFixPrompt",
projectManager.getProps().getBoolean(
ValidatorConfigs.VALIDATOR_AUTO_FIX_PROMPT_FLAG_PARAM,
ValidatorConfigs.DEFAULT_VALIDATOR_AUTO_FIX_PROMPT_FLAG));
page.add(
"validatorFixLabel",
projectManager.getProps().get(
ValidatorConfigs.VALIDATOR_AUTO_FIX_PROMPT_LABEL_PARAM));
page.add(
"validatorFixLink",
projectManager.getProps().get(
ValidatorConfigs.VALIDATOR_AUTO_FIX_PROMPT_LINK_PARAM));
boolean adminPerm = perm.isPermissionSet(Type.ADMIN);
if (adminPerm) {
page.add("admin", true);
}
// Set this so we can display execute buttons only to those who have
// access.
if (perm.isPermissionSet(Type.EXECUTE) || adminPerm) {
page.add("exec", true);
} else {
page.add("exec", false);
}
List<Flow> flows = project.getFlows();
if (!flows.isEmpty()) {
Collections.sort(flows, FLOW_ID_COMPARATOR);
page.add("flows", flows);
}
}
} catch (AccessControlException e) {
page.add("errorMsg", e.getMessage());
}
page.render();
}
private void handleCreate(HttpServletRequest req, HttpServletResponse resp,
Session session) throws ServletException {
String projectName = hasParam(req, "name") ? getParam(req, "name") : null;
String projectDescription =
hasParam(req, "description") ? getParam(req, "description") : null;
logger.info("Create project " + projectName);
User user = session.getUser();
String status = null;
String action = null;
String message = null;
HashMap<String, Object> params = null;
if (lockdownCreateProjects && !hasPermissionToCreateProject(user)) {
message =
"User " + user.getUserId()
+ " doesn't have permission to create projects.";
logger.info(message);
status = "error";
} else {
try {
projectManager.createProject(projectName, projectDescription, user);
status = "success";
action = "redirect";
String redirect = "manager?project=" + projectName;
params = new HashMap<String, Object>();
params.put("path", redirect);
} catch (ProjectManagerException e) {
message = e.getMessage();
status = "error";
}
}
String response = createJsonResponse(status, message, action, params);
try {
Writer write = resp.getWriter();
write.append(response);
write.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
private void registerError(Map<String, String> ret, String error,
HttpServletResponse resp, int returnCode) {
ret.put("error", error);
resp.setStatus(returnCode);
}
private void ajaxHandleUpload(HttpServletRequest req, HttpServletResponse resp,
Map<String, String> ret, Map<String, Object> multipart, Session session)
throws ServletException, IOException {
User user = session.getUser();
String projectName = (String) multipart.get("project");
Project project = projectManager.getProject(projectName);
String autoFix = (String) multipart.get("fix");
Props props = new Props();
if (autoFix != null && autoFix.equals("off")) {
props.put(ValidatorConfigs.CUSTOM_AUTO_FIX_FLAG_PARAM, "false");
} else {
props.put(ValidatorConfigs.CUSTOM_AUTO_FIX_FLAG_PARAM, "true");
}
if (lockdownUploadProjects) {
registerError(ret, "Project uploading is locked out", resp, 400);
} else if (projectName == null || projectName.isEmpty()) {
registerError(ret, "No project name found.", resp, 400);
} else if (project == null) {
registerError(ret, "Installation Failed. Project '" + projectName
+ "' doesn't exist.", resp, 400);
} else if (!hasPermission(project, user, Type.WRITE)) {
registerError(ret, "Installation Failed. User '" + user.getUserId()
+ "' does not have write access.", resp, 400);
} else {
ret.put("projectId", String.valueOf(project.getId()));
FileItem item = (FileItem) multipart.get("file");
String name = item.getName();
String type = null;
final String contentType = item.getContentType();
if (contentType != null
&& (contentType.startsWith(APPLICATION_ZIP_MIME_TYPE)
|| contentType.startsWith("application/x-zip-compressed") || contentType
.startsWith("application/octet-stream"))) {
type = "zip";
} else {
item.delete();
registerError(ret, "File type " + contentType + " unrecognized.", resp, 400);
return;
}
File tempDir = Utils.createTempDir();
OutputStream out = null;
try {
logger.info("Uploading file " + name);
File archiveFile = new File(tempDir, name);
out = new BufferedOutputStream(new FileOutputStream(archiveFile));
IOUtils.copy(item.getInputStream(), out);
out.close();
Map<String, ValidationReport> reports =
projectManager.uploadProject(project, archiveFile, type, user,
props);
StringBuffer errorMsgs = new StringBuffer();
StringBuffer warnMsgs = new StringBuffer();
for (Entry<String, ValidationReport> reportEntry : reports.entrySet()) {
ValidationReport report = reportEntry.getValue();
if (!report.getInfoMsgs().isEmpty()) {
for (String msg : report.getInfoMsgs()) {
switch (ValidationReport.getInfoMsgLevel(msg)) {
case ERROR:
errorMsgs.append(ValidationReport.getInfoMsg(msg) + "<br/>");
break;
case WARN:
warnMsgs.append(ValidationReport.getInfoMsg(msg) + "<br/>");
break;
default:
break;
}
}
}
if (!report.getErrorMsgs().isEmpty()) {
errorMsgs.append("Validator " + reportEntry.getKey()
+ " reports errors:<ul>");
for (String msg : report.getErrorMsgs()) {
errorMsgs.append("<li>" + msg + "</li>");
}
errorMsgs.append("</ul>");
}
if (!report.getWarningMsgs().isEmpty()) {
warnMsgs.append("Validator " + reportEntry.getKey()
+ " reports warnings:<ul>");
for (String msg : report.getWarningMsgs()) {
warnMsgs.append("<li>" + msg + "</li>");
}
warnMsgs.append("</ul>");
}
}
if (errorMsgs.length() > 0) {
// If putting more than 4000 characters in the cookie, the entire
// message
// will somehow get discarded.
registerError(ret, errorMsgs.length() > 4000 ? errorMsgs.substring(0, 4000)
: errorMsgs.toString(), resp, 500);
}
if (warnMsgs.length() > 0) {
ret.put(
"warn",
warnMsgs.length() > 4000 ? warnMsgs.substring(0, 4000) : warnMsgs
.toString());
}
} catch (Exception e) {
logger.info("Installation Failed.", e);
String error = e.getMessage();
if (error.length() > 512) {
error =
error.substring(0, 512) + "<br>Too many errors to display.<br>";
}
registerError(ret, "Installation Failed.<br>" + error, resp , 500);
} finally {
if (out != null) {
out.close();
}
if (tempDir.exists()) {
FileUtils.deleteDirectory(tempDir);
}
}
ret.put("version", String.valueOf(project.getVersion()));
}
}
private void handleUpload(HttpServletRequest req, HttpServletResponse resp,
Map<String, Object> multipart, Session session) throws ServletException,
IOException {
HashMap<String, String> ret = new HashMap<String, String>();
String projectName = (String) multipart.get("project");
ajaxHandleUpload(req, resp, ret, multipart, session);
if (ret.containsKey("error")) {
setErrorMessageInCookie(resp, ret.get("error"));
}
if (ret.containsKey("warn")) {
setWarnMessageInCookie(resp, ret.get("warn"));
}
resp.sendRedirect(req.getRequestURI() + "?project=" + projectName);
}
private static class NodeLevelComparator implements Comparator<Node> {
@Override
public int compare(Node node1, Node node2) {
return node1.getLevel() - node2.getLevel();
}
}
public static class PageSelection {
private String page;
private int size;
private boolean disabled;
private boolean selected;
private int nextPage;
public PageSelection(String pageName, int size, boolean disabled,
boolean selected, int nextPage) {
this.page = pageName;
this.size = size;
this.disabled = disabled;
this.setSelected(selected);
this.nextPage = nextPage;
}
public String getPage() {
return page;
}
public int getSize() {
return size;
}
public boolean getDisabled() {
return disabled;
}
public boolean isSelected() {
return selected;
}
public int getNextPage() {
return nextPage;
}
public void setSelected(boolean selected) {
this.selected = selected;
}
}
private Permission getPermissionObject(Project project, User user,
Permission.Type type) {
Permission perm = project.getCollectivePermission(user);
for (String roleName : user.getRoles()) {
Role role = userManager.getRole(roleName);
perm.addPermissions(role.getPermission());
}
return perm;
}
private boolean hasPermissionToCreateProject(User user) {
for (String roleName : user.getRoles()) {
Role role = userManager.getRole(roleName);
Permission perm = role.getPermission();
if (perm.isPermissionSet(Permission.Type.ADMIN)
|| perm.isPermissionSet(Permission.Type.CREATEPROJECTS)) {
return true;
}
}
return false;
}
private void handleReloadProjectWhitelist(HttpServletRequest req,
HttpServletResponse resp, Session session) throws IOException {
HashMap<String, Object> ret = new HashMap<String, Object>();
if (hasPermission(session.getUser(), Permission.Type.ADMIN)) {
try {
if (projectManager.loadProjectWhiteList()) {
ret.put("success", "Project whitelist re-loaded!");
} else {
ret.put("error", "azkaban.properties doesn't contain property "
+ ProjectWhitelist.XML_FILE_PARAM);
}
} catch (Exception e) {
ret.put("error",
"Exception occurred while trying to re-load project whitelist: "
+ e);
}
} else {
ret.put("error", "Provided session doesn't have admin privilege.");
}
this.writeJSON(resp, ret);
}
protected boolean hasPermission(User user, Permission.Type type) {
for (String roleName : user.getRoles()) {
Role role = userManager.getRole(roleName);
if (role.getPermission().isPermissionSet(type)
|| role.getPermission().isPermissionSet(Permission.Type.ADMIN)) {
return true;
}
}
return false;
}
}