azkaban-aplcache

Adding Projects and permissions.

5/9/2012 12:15:36 AM

Details

diff --git a/conf/azkaban.properties b/conf/azkaban.properties
index 960d0d0..0eabd49 100644
--- a/conf/azkaban.properties
+++ b/conf/azkaban.properties
@@ -11,7 +11,8 @@ user.manager.class=azkaban.user.XmlUserManager
 user.manager.xml.file=conf/azkaban-users.xml
 
 #Loader for projects
-project.loader.class=azkaban.project.FileProjectLoader
+project.manager.class=azkaban.project.FileProjectManager
+file.project.loader.path=projects
 project.global.properties=conf/global.properties
 
 # Velocity dev mode
diff --git a/src/java/azkaban/project/FileProjectLoader.java b/src/java/azkaban/project/FileProjectLoader.java
index eb59751..373d845 100644
--- a/src/java/azkaban/project/FileProjectLoader.java
+++ b/src/java/azkaban/project/FileProjectLoader.java
@@ -4,12 +4,36 @@ import java.io.File;
 import java.util.HashMap;
 import java.util.Map;
 
+import org.apache.log4j.Logger;
+
 import azkaban.utils.Props;
 
+/**
+ * ProjectManager that stores everything on local file system.
+ * The following global parameters should be set -
+ * file.project.loader.path - The project install path where projects will be loaded installed to.
+ */
 public class FileProjectLoader implements ProjectLoader {
-
+	public static final String DIRECTORY_PARAM = "file.project.loader.path";
+    private static final Logger logger = Logger.getLogger(FileProjectLoader.class);
+	private File projectDirectory;
+	
     public FileProjectLoader(Props props) {
-    	
+    	String projectDir = props.getString(DIRECTORY_PARAM);
+		logger.info("Using directory " + projectDir + " as the project directory.");
+    	projectDirectory = new File(projectDir);
+    	if (!projectDirectory.exists()) {
+    		logger.info("Directory " + projectDir + " doesn't exist. Creating.");
+    		if (projectDirectory.mkdirs()) {
+    			logger.info("Directory creation was successful.");
+    		}
+    		else {
+    			throw new RuntimeException("FileProjectLoader cannot create directory " + projectDirectory);
+    		}
+    	}
+    	else if (projectDirectory.isFile()) {
+			throw new RuntimeException("FileProjectManager directory " + projectDirectory + " is really a file.");
+    	}
     }
     
     @Override
@@ -19,8 +43,7 @@ public class FileProjectLoader implements ProjectLoader {
     }
 
     @Override
-    public void addProject(Project project, File directory) {
-        // TODO Auto-generated method stub
+    public void addProject(Project project) {
         
     }
 
@@ -30,10 +53,5 @@ public class FileProjectLoader implements ProjectLoader {
         return false;
     }
 
-    @Override
-    public void init(Props props) {
-        // TODO Auto-generated method stub
-        
-    }
     
 }
\ No newline at end of file
diff --git a/src/java/azkaban/project/FileProjectManager.java b/src/java/azkaban/project/FileProjectManager.java
new file mode 100644
index 0000000..315cb0b
--- /dev/null
+++ b/src/java/azkaban/project/FileProjectManager.java
@@ -0,0 +1,83 @@
+package azkaban.project;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.log4j.Logger;
+
+import azkaban.user.User;
+import azkaban.utils.Props;
+
+/**
+ * A project loader that stores everything on local file system.
+ * The following global parameters should be set -
+ * file.project.loader.path - The project install path where projects will be loaded installed to.
+ */
+public class FileProjectManager implements ProjectManager {
+	public static final String DIRECTORY_PARAM = "file.project.loader.path";
+    private static final Logger logger = Logger.getLogger(FileProjectManager.class);
+    private ConcurrentHashMap<String, Project> projects = new ConcurrentHashMap<String, Project>();
+	private File projectDirectory;
+	
+    public FileProjectManager(Props props) {
+    	setupDirectories(props);
+    }
+
+    private void setupDirectories(Props props) {
+    	String projectDir = props.getString(DIRECTORY_PARAM);
+		logger.info("Using directory " + projectDir + " as the project directory.");
+    	projectDirectory = new File(projectDir);
+    	if (!projectDirectory.exists()) {
+    		logger.info("Directory " + projectDir + " doesn't exist. Creating.");
+    		if (projectDirectory.mkdirs()) {
+    			logger.info("Directory creation was successful.");
+    		}
+    		else {
+    			throw new RuntimeException("FileProjectLoader cannot create directory " + projectDirectory);
+    		}
+    	}
+    	else if (projectDirectory.isFile()) {
+			throw new RuntimeException("FileProjectManager directory " + projectDirectory + " is really a file.");
+    	}
+    }
+    
+    public List<String> getProjectNames() {
+        return new ArrayList<String>(projects.keySet());
+    }
+    
+    public Project getProject(String name) {
+    	return projects.get(name);
+    }
+    
+    @Override
+    public synchronized Project createProjects(String projectName, String description, User creator) throws ProjectManagerException {
+    	if (projectName == null || projectName.trim().isEmpty()) {
+    		throw new ProjectManagerException("Project name cannot be empty.");
+    	}
+    	else if (description == null || description.trim().isEmpty()) {
+    		throw new ProjectManagerException("Description cannot be empty.");
+    	}
+    	else if (creator == null) {
+    		throw new ProjectManagerException("Valid creator user must be set.");
+    	}
+
+    	if (projects.contains(projectName)) {
+    		throw new ProjectManagerException("Project already exists.");
+    	}
+    	
+    	Project project = new Project(projectName);
+    	return project;
+    }
+    
+    public Project loadProjects() {
+    	return null;
+    }
+
+	@Override
+	public synchronized Project removeProjects(String projectName) {
+		// TODO Auto-generated method stub
+		return null;
+	}
+}
\ No newline at end of file
diff --git a/src/java/azkaban/project/Project.java b/src/java/azkaban/project/Project.java
index 8cb42b5..24ed79e 100644
--- a/src/java/azkaban/project/Project.java
+++ b/src/java/azkaban/project/Project.java
@@ -1,8 +1,20 @@
 package azkaban.project;
 
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import azkaban.user.Permission;
+import azkaban.user.User;
+
 public class Project {
     private final String name;
-
+    private String description;
+    private long createTimestamp;
+    private long lastModifiedTimestamp;
+    private HashMap<String, Permission> userToPermission = new HashMap<String, Permission>();
+    
     public Project(String name) {
         this.name = name;
     }
@@ -10,4 +22,83 @@ public class Project {
     public String getName() {
         return name;
     }
+    
+    public void setDescription(String description) {
+    	this.description = description;
+    }
+    
+    public String getDescription() {
+    	return description;
+    }
+    
+    public void setUserPermission(String userid, Permission flags) {
+    	userToPermission.put(userid, flags);
+    }
+    
+    public Permission getUserPermission(User user) {
+    	return userToPermission.get(user.getUserId());
+    }
+
+	public long getCreateTimestamp() {
+		return createTimestamp;
+	}
+
+	public void setCreateTimestamp(long createTimestamp) {
+		this.createTimestamp = createTimestamp;
+	}
+
+	public long getLastModifiedTimestamp() {
+		return lastModifiedTimestamp;
+	}
+
+	public void setLastModifiedTimestamp(long lastModifiedTimestamp) {
+		this.lastModifiedTimestamp = lastModifiedTimestamp;
+	}
+	
+    public Object toObject() {
+    	HashMap<String,Object> projectObject = new HashMap<String, Object>();
+    	projectObject.put("name", name);
+    	projectObject.put("description", description);
+    	projectObject.put("createTimestamp", createTimestamp);
+    	projectObject.put("lastModifiedTimestamp",lastModifiedTimestamp);
+    	
+    	ArrayList<Map<String,Object>> users = new ArrayList<Map<String,Object>>();
+    	for (Map.Entry<String, Permission> entry: userToPermission.entrySet()) {
+    		HashMap<String,Object> userMap = new HashMap<String,Object>();
+    		userMap.put("userid", entry.getKey());
+    		userMap.put("permissions", entry.getValue().toStringArray());
+    		users.add(userMap);
+    	}
+ 
+    	projectObject.put("users", users);
+    	return projectObject;
+    }
+    
+    @SuppressWarnings("unchecked")
+	public static Project projectFromObject(Object object) {
+    	Map<String,Object> projectObject = (Map<String,Object>)object;
+    	String name = (String)projectObject.get("name");
+    	String description = (String)projectObject.get("description");
+    	long createTimestamp = (Long)projectObject.get("createTimestamp");
+    	long lastModifiedTimestamp =  (Long)projectObject.get("lastModifiedTimestamp");
+    	
+    	Project project = new Project(name);
+    	project.setDescription(description);
+    	project.setCreateTimestamp(createTimestamp);
+    	project.setLastModifiedTimestamp(lastModifiedTimestamp);
+    	
+    	
+		List<Map<String,Object>> users = (List<Map<String,Object>>)projectObject.get("users");
+    	
+    	for (Map<String, Object> user: users) {
+    		String userid = (String)user.get("userid");
+    		Permission perm = new Permission();
+    		List<String> list = (List<String>)user.get("permissions");
+    		perm.setPermissions(list);
+    		
+    		project.setUserPermission(userid, perm);
+    	}
+    	
+    	return null;
+    }
 }
diff --git a/src/java/azkaban/project/ProjectLoader.java b/src/java/azkaban/project/ProjectLoader.java
index 86433dc..0d6db75 100644
--- a/src/java/azkaban/project/ProjectLoader.java
+++ b/src/java/azkaban/project/ProjectLoader.java
@@ -6,11 +6,10 @@ import java.util.Map;
 import azkaban.utils.Props;
 
 public interface ProjectLoader {
-    public void init(Props props);
 
     public Map<String, Project> loadAllProjects();
 
-    public void addProject(Project project, File directory);
+    public void addProject(Project project);
 
     public boolean removeProject(Project project);
 }
diff --git a/src/java/azkaban/project/ProjectManager.java b/src/java/azkaban/project/ProjectManager.java
index ddc0ea3..de17ea4 100644
--- a/src/java/azkaban/project/ProjectManager.java
+++ b/src/java/azkaban/project/ProjectManager.java
@@ -1,47 +1,16 @@
 package azkaban.project;
 
-import java.util.ArrayList;
 import java.util.List;
-import java.util.concurrent.ConcurrentHashMap;
-
-import org.apache.log4j.Logger;
 
 import azkaban.user.User;
-import azkaban.utils.Props;
 
-public class ProjectManager {
-    private static final Logger logger = Logger.getLogger(ProjectManager.class);
-    private ConcurrentHashMap<String, Project> projects = new ConcurrentHashMap<String, Project>();
-    private ProjectLoader loader;
+public interface ProjectManager {
     
-    public ProjectManager(Props props, ProjectLoader loader) throws Exception {
-    	projects.putAll(loader.loadAllProjects());
-    }
-
-    public List<String> getProjectNames() {
-        return new ArrayList<String>(projects.keySet());
-    }
+    public List<String> getProjectNames();
     
-    public Project getProject(String name) {
-    	return projects.get(name);
-    }
+    public Project getProject(String name);
     
-    public Project createProjects(String projectName, String description, User creator) throws ProjectManagerException {
-    	if (projectName == null || projectName.trim().isEmpty()) {
-    		throw new ProjectManagerException("Project name cannot be empty.");
-    	}
-    	else if (description == null || description.trim().isEmpty()) {
-    		throw new ProjectManagerException("Description cannot be empty.");
-    	}
-    	else if (creator == null) {
-    		throw new ProjectManagerException("Valid creator user must be set.");
-    	}
-    	
-    	
-    	return null;
-    }
+    public Project createProjects(String projectName, String description, User creator) throws ProjectManagerException;
     
-    public Project loadProjects() {
-    	return null;
-    }
+    public Project removeProjects(String projectName) throws ProjectManagerException;
 }
\ No newline at end of file
diff --git a/src/java/azkaban/user/Permission.java b/src/java/azkaban/user/Permission.java
new file mode 100644
index 0000000..81e95de
--- /dev/null
+++ b/src/java/azkaban/user/Permission.java
@@ -0,0 +1,72 @@
+package azkaban.user;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+public class Permission {
+	public static final String[] PERMISSIONS = {"READ", "WRITE", "EXECUTE", "SCHEDULE", "DELETE", "ADMIN"};
+	private static final Map<String, Integer> PERMISSIONS_TO_INDEX = new HashMap<String,Integer>();
+	
+	static {
+		int i = 0;
+		for (String perm: PERMISSIONS) {
+			PERMISSIONS_TO_INDEX.put(perm, i++);
+		}
+	}
+	
+	private boolean[] flags;
+	
+	public Permission() {
+		flags = new boolean[PERMISSIONS.length];
+	}
+	
+	public void setPermissions(String ... list) {
+		for (String perm: list) {
+			Integer index = PERMISSIONS_TO_INDEX.get(perm);
+			if (index != null) {
+				flags[index] = true;
+			}
+		}
+	}
+	
+	public void setPermissions(Collection<String> list) {
+		for (String perm: list) {
+			Integer index = PERMISSIONS_TO_INDEX.get(perm);
+			if (index != null) {
+				flags[index] = true;
+			}
+		}
+	}
+	
+	public void unsetPermissions(String ... list) {
+		for (String perm: list) {
+			Integer index = PERMISSIONS_TO_INDEX.get(perm);
+			if (index != null) {
+				flags[index] = false;
+			}
+		}
+	}
+	
+	public boolean isPermissionSet(String permission) {
+		Integer index = PERMISSIONS_TO_INDEX.get(permission);
+		if (index != null) {
+			return flags[index];
+		}
+
+		return false;
+	}
+	
+	public String[] toStringArray() {
+		ArrayList<String> list = new ArrayList<String>();
+		int count = 0;
+		for (int i = 0; i < flags.length; ++i) {
+			if (flags[i]) {
+				list.add(PERMISSIONS[i]);
+				count++;
+			}
+		}
+		return list.toArray(new String[count]);
+	}
+}
diff --git a/src/java/azkaban/user/User.java b/src/java/azkaban/user/User.java
index aa965e9..2e6859b 100644
--- a/src/java/azkaban/user/User.java
+++ b/src/java/azkaban/user/User.java
@@ -54,4 +54,29 @@ public class User {
 		groupStr += "]";
 		return userid + ": " + groupStr;
 	}
+	
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result + ((userid == null) ? 0 : userid.hashCode());
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (obj == null)
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		User other = (User) obj;
+		if (userid == null) {
+			if (other.userid != null)
+				return false;
+		} else if (!userid.equals(other.userid))
+			return false;
+		return true;
+	}
 }
diff --git a/src/java/azkaban/user/XmlUserManager.java b/src/java/azkaban/user/XmlUserManager.java
index 8b91fe3..8e0b30e 100644
--- a/src/java/azkaban/user/XmlUserManager.java
+++ b/src/java/azkaban/user/XmlUserManager.java
@@ -48,7 +48,7 @@ public class XmlUserManager implements UserManager {
 	private HashMap<String, String> userPassword;
 	
 	/**
-	 * The constructor. Takes
+	 * The constructor.
 	 * 
 	 * @param props
 	 */
diff --git a/src/java/azkaban/webapp/AzkabanWebServer.java b/src/java/azkaban/webapp/AzkabanWebServer.java
index ff174aa..150a962 100644
--- a/src/java/azkaban/webapp/AzkabanWebServer.java
+++ b/src/java/azkaban/webapp/AzkabanWebServer.java
@@ -36,6 +36,7 @@ import org.mortbay.jetty.servlet.ServletHolder;
 import org.mortbay.thread.QueuedThreadPool;
 
 import azkaban.project.FileProjectLoader;
+import azkaban.project.FileProjectManager;
 import azkaban.project.ProjectLoader;
 import azkaban.project.ProjectManager;
 import azkaban.user.UserManager;
@@ -63,7 +64,7 @@ import joptsimple.OptionSpec;
  * default.timezone.id - The timezone code. I.E. America/Los Angeles
  * 
  * user.manager.class - The UserManager class used for the user manager. Default is XmlUserManager.
- * project.loader.class - The ProjectLoader class used by the ProjectManager to load classes.
+ * project.manager.class - The ProjectManager to load projects
  * project.global.properties - The base properties inherited by all projects and jobs
  * 
  * jetty.maxThreads - # of threads for jetty
@@ -86,7 +87,7 @@ public class AzkabanWebServer {
     private static final int DEFAULT_THREAD_NUMBER = 10;
     private static final String VELOCITY_DEV_MODE_PARAM = "velocity.dev.mode";
     private static final String USER_MANAGER_CLASS_PARAM = "user.manager.class";
-    private static final String PROJECT_LOADER_CLASS_PARAM = "project.loader.class";
+    private static final String PROJECT_MANAGER_CLASS_PARAM = "project.manager.class";
     private static final String DEFAULT_STATIC_DIR = "";
 
     private final VelocityEngine velocityEngine;
@@ -154,43 +155,28 @@ public class AzkabanWebServer {
     }
 
     private ProjectManager loadProjectManager(Props props) {
+        Class<?> projectManagerClass = props.getClass(PROJECT_MANAGER_CLASS_PARAM, null);
+        logger.info("Loading project manager class " + projectManagerClass.getName());
         ProjectManager manager = null;
-        try {
-        	ProjectLoader projectLoader = loadProjectLoader(props);
-            manager = new ProjectManager(props, projectLoader);
-        }
-        catch(Exception e) {
-            logger.error(e);
-            throw new RuntimeException(e);
-        }
-        
-        return manager;
-    }
-    
-    private ProjectLoader loadProjectLoader(Props props) {
-        Class<?> projectLoaderClass = props.getClass(PROJECT_LOADER_CLASS_PARAM,null);
-        logger.info("Loading project loader class " + projectLoaderClass.getName());
-        ProjectLoader loader = null;
 
-        if (projectLoaderClass != null
-            && projectLoaderClass.getConstructors().length > 0) {
+        if (projectManagerClass != null
+            && projectManagerClass.getConstructors().length > 0) {
 
         	try {
-        		Constructor<?> projectLoaderConstructor = projectLoaderClass.getConstructor(Props.class);
-        		loader = (ProjectLoader)projectLoaderConstructor.newInstance(props);
+        		Constructor<?> projectManagerConstructor = projectManagerClass.getConstructor(Props.class);
+        		manager = (ProjectManager)projectManagerConstructor.newInstance(props);
         	}
         	catch (Exception e) {
-        	      logger.error("Could not instantiate UserManager "
-                          + projectLoaderClass.getName());
+        	      logger.error("Could not instantiate ProjectManager "
+                          + projectManagerClass.getName());
                   throw new RuntimeException(e);
         	}
 
         } else {
-        	logger.info("By default, using FileProjectLoader");
-        	loader = new FileProjectLoader(props);
+            manager = new FileProjectManager(props);
         }
 
-        return loader;
+        return manager;
     }
     
     /**
diff --git a/src/java/azkaban/webapp/servlet/AzkabanServletContextListener.java b/src/java/azkaban/webapp/servlet/AzkabanServletContextListener.java
index 3877adb..e1e99f7 100644
--- a/src/java/azkaban/webapp/servlet/AzkabanServletContextListener.java
+++ b/src/java/azkaban/webapp/servlet/AzkabanServletContextListener.java
@@ -44,5 +44,4 @@ public class AzkabanServletContextListener implements ServletContextListener {
 
         event.getServletContext().setAttribute(AZKABAN_SERVLET_CONTEXT_KEY, this.app);
     }
-
 }
diff --git a/src/java/azkaban/webapp/servlet/LoginAbstractAzkabanServlet.java b/src/java/azkaban/webapp/servlet/LoginAbstractAzkabanServlet.java
index 1c915d8..76ec115 100644
--- a/src/java/azkaban/webapp/servlet/LoginAbstractAzkabanServlet.java
+++ b/src/java/azkaban/webapp/servlet/LoginAbstractAzkabanServlet.java
@@ -1,6 +1,7 @@
 package azkaban.webapp.servlet;
 
 import java.io.IOException;
+import java.io.Writer;
 import java.util.UUID;
 
 import javax.servlet.ServletException;
@@ -74,8 +75,7 @@ public abstract class LoginAbstractAzkabanServlet extends
     private void handleLogin(HttpServletRequest req, HttpServletResponse resp,
             String errorMsg) throws ServletException, IOException {
 
-        Page page = newPage(req, resp,
-                "azkaban/webapp/servlet/velocity/login.vm");
+        Page page = newPage(req, resp, "azkaban/webapp/servlet/velocity/login.vm");
         if (errorMsg != null) {
             page.add("errorMsg", errorMsg);
         }
@@ -109,12 +109,24 @@ public abstract class LoginAbstractAzkabanServlet extends
                     getApplication().getSessionCache().addSession(session);
                     handleGet(req, resp, session);
                 } else {
-                    handleLogin(req, resp, "Enter username and password");
+                	if (isAjaxCall(req)) {
+                		String response = createJsonResponse("error", "Incorrect Login.", "login", null);
+                		writeResponse(resp, response);
+                	}
+                	else {
+                		handleLogin(req, resp, "Enter username and password");
+                	}
                 }
             } else {
                 Session session = getSessionFromRequest(req);
                 if (session == null) {
-                	handleLogin(req, resp, "Invalid session");
+                	if (isAjaxCall(req)) {
+                		String response = createJsonResponse("error", "Invalid Session. Need to re-login", "login", null);
+                		writeResponse(resp, response);
+                	}
+                	else {
+                		handleLogin(req, resp, "Enter username and password");
+                	}
                 } else {
                 	handlePost(req, resp, session);
                 }
@@ -122,13 +134,35 @@ public abstract class LoginAbstractAzkabanServlet extends
         } else {
             Session session = getSessionFromRequest(req);
             if (session == null) {
-                handleLogin(req, resp);
+            	if (isAjaxCall(req)) {
+            		String response = createJsonResponse("error", "Invalid Session. Need to re-login", "login", null);
+            		writeResponse(resp, response);
+            	}
+            	else {
+            		handleLogin(req, resp, "Enter username and password");
+            	}
             } else {
             	handlePost(req, resp, session);
             }
         }
     }
     
+    protected void writeResponse(HttpServletResponse resp, String response) throws IOException {
+    	Writer writer = resp.getWriter();
+    	writer.append(response);
+    	writer.flush();
+    }
+    
+    protected boolean isAjaxCall(HttpServletRequest req) throws ServletException {
+    	String value = req.getHeader("X-Requested-With");
+    	if (value != null) {
+    		logger.info("has X-Requested-With " + value);
+     		return value.equals("XMLHttpRequest");
+    	}
+    	
+    	return false;
+    }
+    
     /** 
      * The get request is handed off to the implementor after the user is logged in.
      * 
diff --git a/src/java/azkaban/webapp/servlet/ProjectManagerServlet.java b/src/java/azkaban/webapp/servlet/ProjectManagerServlet.java
index 77cf86f..2f52bbd 100644
--- a/src/java/azkaban/webapp/servlet/ProjectManagerServlet.java
+++ b/src/java/azkaban/webapp/servlet/ProjectManagerServlet.java
@@ -54,34 +54,36 @@ public class ProjectManagerServlet extends LoginAbstractAzkabanServlet {
     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();
-//    	HashMap<String, Object> responseObj = new HashMap<String, Object>();
-//    	String status = null;
-//    	String redirect = null;
-//    	String message = null;
-//
-//    	try {
-//			manager.createProjects(projectName, projectDescription, user);
-//			status = "success";
-//			redirect = "manager?project=" + projectName;
-//		} catch (ProjectManagerException e) {
-//			message = e.getMessage();
-//			status = "error";
-//		}
-//    	
-//    	String response = createJsonResponse(status, message, redirect, null);
-//    	try {
-//			Writer write = resp.getWriter();
-//			write.append(response);
-//			write.flush();
-//    	} catch (IOException e) {
-//			// TODO Auto-generated catch block
-//			e.printStackTrace();
-//		}
+    	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;
+    	try {
+			manager.createProjects(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();
+		}
     }
 
 }
diff --git a/src/java/azkaban/webapp/servlet/velocity/index.vm b/src/java/azkaban/webapp/servlet/velocity/index.vm
index 06987e7..5dbf298 100644
--- a/src/java/azkaban/webapp/servlet/velocity/index.vm
+++ b/src/java/azkaban/webapp/servlet/velocity/index.vm
@@ -44,26 +44,31 @@
     <h3>Create Project</h3>
       <div id="errorMsg" class="box-error-message">$errorMsg</div>
       <div class="message">
-		  <form id="deployform" method="post" action="$!context">
-	          <fieldset>
-	            <dl>
-	              <dt><label for="path">Project Name</label></dt>
-	              <dd><input id="path" name="project" type="text" size="20" title="The project name."/>
-	              </dd>
-	              <dt>Description</dt>
-	              <dd><textarea id="description" name="description" rows="2" cols="40"></textarea></dd>
-	
-	              <input name="action" type="hidden" value="create" />
-	              <input name="redirect" type="hidden" value="$!context/" />
-	            </dl>
-	          </fieldset>
-		  </form>
+          <fieldset>
+            <dl>
+              <dt><label for="path">Project Name</label></dt>
+              <dd><input id="path" name="project" type="text" size="20" title="The project name."/>
+              </dd>
+              <dt>Description</dt>
+              <dd><textarea id="description" name="description" rows="2" cols="40"></textarea></dd>
+
+              <input name="action" type="hidden" value="create" />
+              <input name="redirect" type="hidden" value="$!context/" />
+            </dl>
+          </fieldset>
       </div>
       <div class="actions">
         <a class="yes btn2" id="create-btn" href="#">Create Project</a>
         <a class="no simplemodal-close btn3" href="#">Cancel</a>
       </div>
     </div>
+    <div id="invalid-session" class="modal">
+      <h3>Invalid Session</h3>
+      <p>Session has expired. Please re-login.</p>
+      <div class="actions">
+          <a class="yes btn2" id="login-btn" href="#">Re-login</a>
+      </div>
+    </div>
 </html>
 
 
diff --git a/src/web/js/azkaban.job.view.js b/src/web/js/azkaban.job.view.js
index b89abfb..2670304 100644
--- a/src/web/js/azkaban.job.view.js
+++ b/src/web/js/azkaban.job.view.js
@@ -63,8 +63,35 @@ azkaban.CreateProjectView= Backbone.View.extend({
     $("#errorMsg").hide();
   },
   handleCreateProject : function(evt) {
-     console.log("Deploying");
-	 $("#deployform").submit();
+	 // First make sure we can upload
+	 var projectName = $('#path').val();
+	 var description = $('#description').val();
+
+     console.log("Creating");
+     $.ajax({
+     	async: "false",
+     	url: "manager",
+     	dataType: "json",
+     	type: "POST",
+     	data: {action:"create", name:projectName, description:description},
+     	success: function(data) {
+     		if (data.status == "success") {
+     			if (data.action == "redirect") {
+     				window.location = data.path;
+     			}
+     		}
+     		else {
+     			if (data.action == "login") {
+ 					window.location = "";
+     			}
+     			else {
+	     			$("#errorMsg").text("ERROR: " + data.message);
+	    			$("#errorMsg").slideDown("fast");
+    			}
+     		}
+     	}
+     });
+
   },
   render: function() {
   }