You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by bh...@apache.org on 2012/12/11 22:39:20 UTC

[2/2] git commit: Refactor ListProjectsCmd and ProjectResponse.

Refactor ListProjectsCmd and ProjectResponse.


Project: http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/commit/8735716b
Tree: http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/tree/8735716b
Diff: http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/diff/8735716b

Branch: refs/heads/api_refactoring
Commit: 8735716ba02c89dfd33ad86748a665b4ab081f96
Parents: c83323f
Author: Min Chen <mi...@citrix.com>
Authored: Tue Dec 11 12:29:00 2012 -0800
Committer: Rohit Yadav <bh...@apache.org>
Committed: Tue Dec 11 12:51:08 2012 -0800

----------------------------------------------------------------------
 api/src/com/cloud/projects/ProjectService.java     |    4 +-
 .../apache/cloudstack/api/ResponseGenerator.java   |    3 +
 .../api/command/user/project/ListProjectsCmd.java  |   10 +-
 .../cloudstack/api/response/ProjectResponse.java   |   19 +-
 .../cloudstack/api/view/vo/ProjectJoinVO.java      |  395 +++++++++++
 server/src/com/cloud/api/ApiDBUtils.java           |   17 +
 server/src/com/cloud/api/ApiResponseHelper.java    |   43 +-
 .../configuration/DefaultComponentLibrary.java     |    2 +
 .../src/com/cloud/projects/ProjectManagerImpl.java |  542 ++++++++-------
 .../src/com/cloud/projects/dao/ProjectJoinDao.java |   36 +
 .../com/cloud/projects/dao/ProjectJoinDaoImpl.java |  130 ++++
 .../com/cloud/projects/MockProjectManagerImpl.java |    6 +-
 setup/db/create-schema.sql                         |   33 +
 13 files changed, 939 insertions(+), 301 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/8735716b/api/src/com/cloud/projects/ProjectService.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/projects/ProjectService.java b/api/src/com/cloud/projects/ProjectService.java
index c0ec91b..2ef367b 100644
--- a/api/src/com/cloud/projects/ProjectService.java
+++ b/api/src/com/cloud/projects/ProjectService.java
@@ -19,6 +19,8 @@ package com.cloud.projects;
 import java.util.List;
 import java.util.Map;
 
+import org.apache.cloudstack.api.view.vo.ProjectJoinVO;
+
 import com.cloud.exception.ConcurrentOperationException;
 import com.cloud.exception.ResourceAllocationException;
 import com.cloud.exception.ResourceUnavailableException;
@@ -61,7 +63,7 @@ public interface ProjectService {
      */
     Project getProject(long id);
 
-    Pair<List<? extends Project>, Integer> listProjects(Long id, String name, String displayText, String state, String accountName,
+    Pair<List<ProjectJoinVO>, Integer> listProjects(Long id, String name, String displayText, String state, String accountName,
             Long domainId, String keyword, Long startIndex, Long pageSize, boolean listAll, boolean isRecursive, Map<String, String> tags);
 
     ProjectAccount assignAccountToProject(Project project, long accountId, Role accountRole);

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/8735716b/api/src/org/apache/cloudstack/api/ResponseGenerator.java
----------------------------------------------------------------------
diff --git a/api/src/org/apache/cloudstack/api/ResponseGenerator.java b/api/src/org/apache/cloudstack/api/ResponseGenerator.java
index acb548a..383ecf8 100644
--- a/api/src/org/apache/cloudstack/api/ResponseGenerator.java
+++ b/api/src/org/apache/cloudstack/api/ResponseGenerator.java
@@ -153,6 +153,7 @@ import com.cloud.vm.InstanceGroup;
 import org.apache.cloudstack.api.view.vo.DomainRouterJoinVO;
 import org.apache.cloudstack.api.view.vo.EventJoinVO;
 import org.apache.cloudstack.api.view.vo.InstanceGroupJoinVO;
+import org.apache.cloudstack.api.view.vo.ProjectJoinVO;
 import org.apache.cloudstack.api.view.vo.ResourceTagJoinVO;
 import org.apache.cloudstack.api.view.vo.SecurityGroupJoinVO;
 import org.apache.cloudstack.api.view.vo.UserAccountJoinVO;
@@ -291,6 +292,8 @@ public interface ResponseGenerator {
 
     ProjectResponse createProjectResponse(Project project);
 
+    List<ProjectResponse> createProjectResponse(ProjectJoinVO... projects);
+
     List<TemplateResponse> createIsoResponses(VirtualMachineTemplate iso, long zoneId, boolean readyOnly);
 
     List<TemplateResponse> createTemplateResponses(long templateId, Long vmId);

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/8735716b/api/src/org/apache/cloudstack/api/command/user/project/ListProjectsCmd.java
----------------------------------------------------------------------
diff --git a/api/src/org/apache/cloudstack/api/command/user/project/ListProjectsCmd.java b/api/src/org/apache/cloudstack/api/command/user/project/ListProjectsCmd.java
index 2350a2c..cc1ccdd 100644
--- a/api/src/org/apache/cloudstack/api/command/user/project/ListProjectsCmd.java
+++ b/api/src/org/apache/cloudstack/api/command/user/project/ListProjectsCmd.java
@@ -32,6 +32,8 @@ import org.apache.cloudstack.api.Implementation;
 import org.apache.cloudstack.api.Parameter;
 import org.apache.cloudstack.api.response.ListResponse;
 import org.apache.cloudstack.api.response.ProjectResponse;
+import org.apache.cloudstack.api.view.vo.ProjectJoinVO;
+
 import com.cloud.exception.InvalidParameterValueException;
 import com.cloud.projects.Project;
 import com.cloud.utils.Pair;
@@ -107,15 +109,11 @@ public class ListProjectsCmd extends BaseListAccountResourcesCmd {
 
     @Override
     public void execute(){
-        Pair<List<? extends Project>, Integer> projects = _projectService.listProjects(id, name, displayText, state,
+        Pair<List<ProjectJoinVO>, Integer> projects = _projectService.listProjects(id, name, displayText, state,
                 this.getAccountName(), this.getDomainId(), this.getKeyword(), this.getStartIndex(), this.getPageSizeVal(),
                 this.listAll(), this.isRecursive(), getTags());
         ListResponse<ProjectResponse> response = new ListResponse<ProjectResponse>();
-        List<ProjectResponse> projectResponses = new ArrayList<ProjectResponse>();
-        for (Project project : projects.first()) {
-            ProjectResponse projectResponse = _responseGenerator.createProjectResponse(project);
-            projectResponses.add(projectResponse);
-        }
+        List<ProjectResponse> projectResponses = _responseGenerator.createProjectResponse(projects.first().toArray(new ProjectJoinVO[projects.first().size()]));
         response.setResponses(projectResponses, projects.second());
         response.setResponseName(getCommandName());
 

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/8735716b/api/src/org/apache/cloudstack/api/response/ProjectResponse.java
----------------------------------------------------------------------
diff --git a/api/src/org/apache/cloudstack/api/response/ProjectResponse.java b/api/src/org/apache/cloudstack/api/response/ProjectResponse.java
index dea1a68..c47e7b6 100644
--- a/api/src/org/apache/cloudstack/api/response/ProjectResponse.java
+++ b/api/src/org/apache/cloudstack/api/response/ProjectResponse.java
@@ -16,6 +16,7 @@
 // under the License.
 package org.apache.cloudstack.api.response;
 
+import java.util.ArrayList;
 import java.util.List;
 
 import org.apache.cloudstack.api.ApiConstants;
@@ -28,7 +29,7 @@ import org.apache.cloudstack.api.BaseResponse;
 public class ProjectResponse extends BaseResponse {
 
     @SerializedName(ApiConstants.ID) @Param(description="the id of the project")
-    private IdentityProxy id = new IdentityProxy("projects");
+    private String id;
 
     @SerializedName(ApiConstants.NAME) @Param(description="the name of the project")
     private String name;
@@ -37,7 +38,7 @@ public class ProjectResponse extends BaseResponse {
     private String displaytext;
 
     @SerializedName(ApiConstants.DOMAIN_ID) @Param(description="the domain id the project belongs to")
-    private IdentityProxy domainId = new IdentityProxy("domain");
+    private String domainId;
 
     @SerializedName(ApiConstants.DOMAIN) @Param(description="the domain name where the project belongs to")
     private String domain;
@@ -49,11 +50,11 @@ public class ProjectResponse extends BaseResponse {
     private String state;
 
     @SerializedName(ApiConstants.TAGS)  @Param(description="the list of resource tags associated with vm", responseObject = ResourceTagResponse.class)
-    private List<ResourceTagResponse> tags;
+    private List<ResourceTagResponse> tags = new ArrayList<ResourceTagResponse>();
 
 
-    public void setId(Long id) {
-        this.id.setValue(id);
+    public void setId(String id) {
+        this.id = id;
     }
 
     public void setName(String name) {
@@ -64,8 +65,8 @@ public class ProjectResponse extends BaseResponse {
         this.displaytext = displaytext;
     }
 
-    public void setDomainId(Long domainId) {
-        this.domainId.setValue(domainId);
+    public void setDomainId(String domainId) {
+        this.domainId = domainId;
     }
 
     public void setDomain(String domain) {
@@ -83,4 +84,8 @@ public class ProjectResponse extends BaseResponse {
     public void setTags(List<ResourceTagResponse> tags) {
         this.tags = tags;
     }
+
+    public void addTag(ResourceTagResponse tag){
+        this.tags.add(tag);
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/8735716b/api/src/org/apache/cloudstack/api/view/vo/ProjectJoinVO.java
----------------------------------------------------------------------
diff --git a/api/src/org/apache/cloudstack/api/view/vo/ProjectJoinVO.java b/api/src/org/apache/cloudstack/api/view/vo/ProjectJoinVO.java
new file mode 100644
index 0000000..f4226d3
--- /dev/null
+++ b/api/src/org/apache/cloudstack/api/view/vo/ProjectJoinVO.java
@@ -0,0 +1,395 @@
+// 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.cloudstack.api.view.vo;
+
+import java.util.Date;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+import javax.persistence.Table;
+
+import com.cloud.server.ResourceTag.TaggedResourceType;
+import com.cloud.utils.db.GenericDao;
+import com.cloud.vm.VirtualMachine.State;
+
+@Entity
+@Table(name="project_view")
+public class ProjectJoinVO extends BaseViewVO {
+
+    @Column(name="uuid")
+    private String uuid;
+
+    @Column(name="name")
+    private String name;
+
+    @Column(name="display_text")
+    String displayText;
+
+
+    @Column(name="owner")
+    String owner;
+
+    @Column(name="state")
+    @Enumerated(value=EnumType.STRING)
+    private State state;
+
+    @Column(name=GenericDao.CREATED_COLUMN)
+    private Date created;
+
+    @Column(name=GenericDao.REMOVED_COLUMN)
+    private Date removed;
+
+    @Column(name="account_id")
+    private long accountId;
+
+    @Column(name="domain_id")
+    private long domainId;
+
+    @Column(name="domain_uuid")
+    private String domainUuid;
+
+    @Column(name="domain_name")
+    private String domainName;
+
+    @Column(name="domain_path")
+    private String domainPath;
+
+    @Column(name="tag_id")
+    private long tagId;
+
+    @Column(name="tag_uuid")
+    private String tagUuid;
+
+    @Column(name="tag_key")
+    private String tagKey;
+
+    @Column(name="tag_value")
+    private String tagValue;
+
+    @Column(name="tag_domain_id")
+    private long tagDomainId;
+
+    @Column(name="tag_account_id")
+    private long tagAccountId;
+
+    @Column(name="tag_resource_id")
+    private long tagResourceId;
+
+    @Column(name="tag_resource_uuid")
+    private String tagResourceUuid;
+
+    @Column(name="tag_resource_type")
+    @Enumerated(value=EnumType.STRING)
+    private TaggedResourceType tagResourceType;
+
+    @Column(name="tag_customer")
+    private String tagCustomer;
+
+
+    public ProjectJoinVO() {
+    }
+
+
+
+
+    public String getUuid() {
+        return uuid;
+    }
+
+
+
+
+    public void setUuid(String uuid) {
+        this.uuid = uuid;
+    }
+
+
+
+    public String getName() {
+        return name;
+    }
+
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+
+
+
+
+
+    public long getDomainId() {
+        return domainId;
+    }
+
+
+    public void setDomainId(long domainId) {
+        this.domainId = domainId;
+    }
+
+
+    public String getDomainUuid() {
+        return domainUuid;
+    }
+
+
+
+
+    public void setDomainUuid(String domainUuid) {
+        this.domainUuid = domainUuid;
+    }
+
+
+
+
+    public String getDomainName() {
+        return domainName;
+    }
+
+
+    public void setDomainName(String domainName) {
+        this.domainName = domainName;
+    }
+
+    public String getDomainPath() {
+        return domainPath;
+    }
+
+
+    public void setDomainPath(String domainPath) {
+        this.domainPath = domainPath;
+    }
+
+
+    public State getState() {
+        return state;
+    }
+
+
+    public void setState(State state) {
+        this.state = state;
+    }
+
+
+    public Date getCreated() {
+        return created;
+    }
+
+
+    public void setCreated(Date created) {
+        this.created = created;
+    }
+
+
+    public Date getRemoved() {
+        return removed;
+    }
+
+
+    public void setRemoved(Date removed) {
+        this.removed = removed;
+    }
+
+
+
+
+    public String getDisplayText() {
+        return displayText;
+    }
+
+
+
+
+    public void setDisplayText(String displayText) {
+        this.displayText = displayText;
+    }
+
+
+
+
+    public String getOwner() {
+        return owner;
+    }
+
+
+
+
+    public void setOwner(String owner) {
+        this.owner = owner;
+    }
+
+
+
+
+    public long getTagId() {
+        return tagId;
+    }
+
+
+
+
+    public void setTagId(long tagId) {
+        this.tagId = tagId;
+    }
+
+
+
+
+    public String getTagUuid() {
+        return tagUuid;
+    }
+
+
+
+
+    public void setTagUuid(String tagUuid) {
+        this.tagUuid = tagUuid;
+    }
+
+
+
+
+    public String getTagKey() {
+        return tagKey;
+    }
+
+
+
+
+    public void setTagKey(String tagKey) {
+        this.tagKey = tagKey;
+    }
+
+
+
+
+    public String getTagValue() {
+        return tagValue;
+    }
+
+
+
+
+    public void setTagValue(String tagValue) {
+        this.tagValue = tagValue;
+    }
+
+
+
+
+    public long getTagDomainId() {
+        return tagDomainId;
+    }
+
+
+
+
+    public void setTagDomainId(long tagDomainId) {
+        this.tagDomainId = tagDomainId;
+    }
+
+
+
+
+    public long getTagAccountId() {
+        return tagAccountId;
+    }
+
+
+
+
+    public void setTagAccountId(long tagAccountId) {
+        this.tagAccountId = tagAccountId;
+    }
+
+
+
+
+    public long getTagResourceId() {
+        return tagResourceId;
+    }
+
+
+
+
+    public void setTagResourceId(long tagResourceId) {
+        this.tagResourceId = tagResourceId;
+    }
+
+
+
+
+    public String getTagResourceUuid() {
+        return tagResourceUuid;
+    }
+
+
+
+
+    public void setTagResourceUuid(String tagResourceUuid) {
+        this.tagResourceUuid = tagResourceUuid;
+    }
+
+
+
+
+    public TaggedResourceType getTagResourceType() {
+        return tagResourceType;
+    }
+
+
+
+
+    public void setTagResourceType(TaggedResourceType tagResourceType) {
+        this.tagResourceType = tagResourceType;
+    }
+
+
+
+
+    public String getTagCustomer() {
+        return tagCustomer;
+    }
+
+
+
+
+    public void setTagCustomer(String tagCustomer) {
+        this.tagCustomer = tagCustomer;
+    }
+
+
+
+
+    public long getAccountId() {
+        return accountId;
+    }
+
+
+
+
+    public void setAccountId(long accountId) {
+        this.accountId = accountId;
+    }
+
+
+
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/8735716b/server/src/com/cloud/api/ApiDBUtils.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/ApiDBUtils.java b/server/src/com/cloud/api/ApiDBUtils.java
index 3cebe46..a60f91b 100755
--- a/server/src/com/cloud/api/ApiDBUtils.java
+++ b/server/src/com/cloud/api/ApiDBUtils.java
@@ -26,6 +26,7 @@ import org.apache.cloudstack.api.ApiConstants.VMDetails;
 import org.apache.cloudstack.api.response.DomainRouterResponse;
 import org.apache.cloudstack.api.response.EventResponse;
 import org.apache.cloudstack.api.response.InstanceGroupResponse;
+import org.apache.cloudstack.api.response.ProjectResponse;
 import org.apache.cloudstack.api.response.ResourceTagResponse;
 import org.apache.cloudstack.api.response.SecurityGroupResponse;
 import org.apache.cloudstack.api.response.UserResponse;
@@ -33,6 +34,7 @@ import org.apache.cloudstack.api.response.UserVmResponse;
 import org.apache.cloudstack.api.view.vo.DomainRouterJoinVO;
 import org.apache.cloudstack.api.view.vo.EventJoinVO;
 import org.apache.cloudstack.api.view.vo.InstanceGroupJoinVO;
+import org.apache.cloudstack.api.view.vo.ProjectJoinVO;
 import org.apache.cloudstack.api.view.vo.ResourceTagJoinVO;
 import org.apache.cloudstack.api.view.vo.SecurityGroupJoinVO;
 import org.apache.cloudstack.api.view.vo.UserAccountJoinVO;
@@ -114,6 +116,7 @@ import com.cloud.offerings.NetworkOfferingVO;
 import com.cloud.offerings.dao.NetworkOfferingDao;
 import com.cloud.projects.Project;
 import com.cloud.projects.ProjectService;
+import com.cloud.projects.dao.ProjectJoinDao;
 import com.cloud.resource.ResourceManager;
 import com.cloud.server.Criteria;
 import com.cloud.server.ManagementServer;
@@ -261,6 +264,7 @@ public class ApiDBUtils {
     private static EventJoinDao _eventJoinDao;
     private static InstanceGroupJoinDao _vmGroupJoinDao;
     private static UserAccountJoinDao _userAccountJoinDao;
+    private static ProjectJoinDao _projectJoinDao;
 
     static {
         _ms = (ManagementServer) ComponentLocator.getComponent(ManagementServer.Name);
@@ -332,6 +336,7 @@ public class ApiDBUtils {
         _counterDao = locator.getDao(CounterDao.class);
         _tagJoinDao = locator.getDao(ResourceTagJoinDao.class);
         _eventJoinDao = locator.getDao(EventJoinDao.class);
+        _projectJoinDao = locator.getDao(ProjectJoinDao.class);
 
         // Note: stats collector should already have been initialized by this time, otherwise a null instance is returned
         _statsCollector = StatsCollector.getInstance();
@@ -1013,4 +1018,16 @@ public class ApiDBUtils {
     public static UserAccountJoinVO newUserView(UserAccount usr){
         return _userAccountJoinDao.newUserView(usr);
     }
+
+    public static ProjectResponse newProjectResponse(ProjectJoinVO proj) {
+        return _projectJoinDao.newProjectResponse(proj);
+    }
+
+    public static ProjectResponse fillProjectDetails(ProjectResponse rsp, ProjectJoinVO proj){
+         return _projectJoinDao.setProjectResponse(rsp,proj);
+    }
+
+    public static List<ProjectJoinVO> newProjectView(Project proj){
+        return _projectJoinDao.newProjectView(proj);
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/8735716b/server/src/com/cloud/api/ApiResponseHelper.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/ApiResponseHelper.java b/server/src/com/cloud/api/ApiResponseHelper.java
index c8c78d0..76fe58d 100755
--- a/server/src/com/cloud/api/ApiResponseHelper.java
+++ b/server/src/com/cloud/api/ApiResponseHelper.java
@@ -116,6 +116,7 @@ import org.apache.cloudstack.api.view.vo.DomainRouterJoinVO;
 import org.apache.cloudstack.api.view.vo.ControlledViewEntity;
 import org.apache.cloudstack.api.view.vo.EventJoinVO;
 import org.apache.cloudstack.api.view.vo.InstanceGroupJoinVO;
+import org.apache.cloudstack.api.view.vo.ProjectJoinVO;
 import org.apache.cloudstack.api.view.vo.ResourceTagJoinVO;
 import org.apache.cloudstack.api.view.vo.SecurityGroupJoinVO;
 import org.apache.cloudstack.api.view.vo.UserAccountJoinVO;
@@ -2671,31 +2672,33 @@ public class ApiResponseHelper implements ResponseGenerator {
 
     @Override
     public ProjectResponse createProjectResponse(Project project) {
-        ProjectResponse response = new ProjectResponse();
-        response.setId(project.getId());
-        response.setName(project.getName());
-        response.setDisplaytext(project.getDisplayText());
-        response.setState(project.getState().toString());
-
-        Domain domain = ApiDBUtils.findDomainById(project.getDomainId());
-        response.setDomainId(domain.getId());
-        response.setDomain(domain.getName());
+        List<ProjectJoinVO> viewPrjs = ApiDBUtils.newProjectView(project);
+        List<ProjectResponse> listPrjs = createProjectResponse(viewPrjs.toArray(new ProjectJoinVO[viewPrjs.size()]));
+        assert listPrjs != null && listPrjs.size() == 1 : "There should be one project  returned";
+        return listPrjs.get(0);
+    }
 
-        response.setOwner(ApiDBUtils.getProjectOwner(project.getId()).getAccountName());
 
-        //set tag information
-        List<? extends ResourceTag> tags = ApiDBUtils.listByResourceTypeAndId(TaggedResourceType.Project, project.getId());
-        List<ResourceTagResponse> tagResponses = new ArrayList<ResourceTagResponse>();
-        for (ResourceTag tag : tags) {
-            ResourceTagResponse tagResponse = createResourceTagResponse(tag, true);
-            tagResponses.add(tagResponse);
+    @Override
+    public List<ProjectResponse> createProjectResponse(ProjectJoinVO... projects) {
+        Hashtable<Long, ProjectResponse> prjDataList = new Hashtable<Long, ProjectResponse>();
+        // Initialise the prjdatalist with the input data
+        for (ProjectJoinVO p : projects) {
+            ProjectResponse pData = prjDataList.get(p.getId());
+            if ( pData == null ){
+                // first time encountering this vm
+                pData = ApiDBUtils.newProjectResponse(p);
+            }
+            else{
+                // update those  1 to many mapping fields
+                pData = ApiDBUtils.fillProjectDetails(pData, p);
+            }
+            prjDataList.put(p.getId(), pData);
         }
-        response.setTags(tagResponses);
-
-        response.setObjectName("project");
-        return response;
+        return new ArrayList<ProjectResponse>(prjDataList.values());
     }
 
+
     @Override
     public FirewallResponse createFirewallResponse(FirewallRule fwRule) {
         FirewallResponse response = new FirewallResponse();

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/8735716b/server/src/com/cloud/configuration/DefaultComponentLibrary.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/configuration/DefaultComponentLibrary.java b/server/src/com/cloud/configuration/DefaultComponentLibrary.java
index c1703bc..af7a84d 100755
--- a/server/src/com/cloud/configuration/DefaultComponentLibrary.java
+++ b/server/src/com/cloud/configuration/DefaultComponentLibrary.java
@@ -139,6 +139,7 @@ import com.cloud.projects.ProjectManagerImpl;
 import com.cloud.projects.dao.ProjectAccountDaoImpl;
 import com.cloud.projects.dao.ProjectDaoImpl;
 import com.cloud.projects.dao.ProjectInvitationDaoImpl;
+import com.cloud.projects.dao.ProjectJoinDaoImpl;
 import com.cloud.resource.ResourceManagerImpl;
 import com.cloud.resourcelimit.ResourceLimitManagerImpl;
 import com.cloud.service.dao.ServiceOfferingDaoImpl;
@@ -229,6 +230,7 @@ public class DefaultComponentLibrary extends ComponentLibraryBase implements Com
         addDao("ResourceTagJoinDao", ResourceTagJoinDaoImpl.class);
         addDao("EventJoinDao", EventJoinDaoImpl.class);
         addDao("UserAccountJoinDao", UserAccountJoinDaoImpl.class);
+        addDao("ProjectJoinDao", ProjectJoinDaoImpl.class);
         ComponentInfo<? extends GenericDao<?, ? extends Serializable>> info = addDao("ServiceOfferingDao", ServiceOfferingDaoImpl.class);
         info.addParameter("cache.size", "50");
         info.addParameter("cache.time.to.live", "600");

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/8735716b/server/src/com/cloud/projects/ProjectManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/projects/ProjectManagerImpl.java b/server/src/com/cloud/projects/ProjectManagerImpl.java
index c922ddb..d9ff0b3 100755
--- a/server/src/com/cloud/projects/ProjectManagerImpl.java
+++ b/server/src/com/cloud/projects/ProjectManagerImpl.java
@@ -5,7 +5,7 @@
 // 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,
@@ -38,6 +38,8 @@ import javax.mail.URLName;
 import javax.mail.internet.InternetAddress;
 import javax.naming.ConfigurationException;
 
+import org.apache.cloudstack.api.view.vo.ProjectJoinVO;
+import org.apache.cloudstack.api.view.vo.UserVmJoinVO;
 import org.apache.log4j.Logger;
 
 import com.cloud.acl.SecurityChecker.AccessType;
@@ -60,6 +62,7 @@ import com.cloud.projects.ProjectAccount.Role;
 import com.cloud.projects.dao.ProjectAccountDao;
 import com.cloud.projects.dao.ProjectDao;
 import com.cloud.projects.dao.ProjectInvitationDao;
+import com.cloud.projects.dao.ProjectJoinDao;
 import com.cloud.server.ResourceTag.TaggedResourceType;
 import com.cloud.tags.ResourceTagVO;
 import com.cloud.tags.dao.ResourceTagDao;
@@ -83,6 +86,7 @@ import com.cloud.utils.db.Filter;
 import com.cloud.utils.db.JoinBuilder;
 import com.cloud.utils.db.SearchBuilder;
 import com.cloud.utils.db.SearchCriteria;
+import com.cloud.utils.db.SearchCriteria.Func;
 import com.cloud.utils.db.SearchCriteria.Op;
 import com.cloud.utils.db.Transaction;
 import com.cloud.utils.exception.CloudRuntimeException;
@@ -95,17 +99,19 @@ public class ProjectManagerImpl implements ProjectManager, Manager{
     public static final Logger s_logger = Logger.getLogger(ProjectManagerImpl.class);
     private String _name;
     private EmailInvite _emailInvite;
-    
+
     @Inject
     private DomainDao _domainDao;
     @Inject
     private ProjectDao _projectDao;
     @Inject
+    private ProjectJoinDao _projectJoinDao;
+    @Inject
     AccountManager _accountMgr;
     @Inject
     DomainManager _domainMgr;
     @Inject
-    ConfigurationManager _configMgr;  
+    ConfigurationManager _configMgr;
     @Inject
     ResourceLimitService _resourceLimitMgr;
     @Inject
@@ -118,24 +124,24 @@ public class ProjectManagerImpl implements ProjectManager, Manager{
     private ProjectInvitationDao _projectInvitationDao;
     @Inject
     protected ResourceTagDao _resourceTagDao;
-    
+
     protected boolean _invitationRequired = false;
     protected long _invitationTimeOut = 86400000;
     protected boolean _allowUserToCreateProject = true;
     protected ScheduledExecutorService _executor;
     protected int _projectCleanupExpInvInterval = 60; //Interval defining how often project invitation cleanup thread is running
-    
-    
+
+
     @Override
     public boolean configure(final String name, final Map<String, Object> params) throws ConfigurationException {
         _name = name;
-        
+
         Map<String, String> configs = _configDao.getConfiguration(params);
         _invitationRequired = Boolean.valueOf(configs.get(Config.ProjectInviteRequired.key()));
         _invitationTimeOut = Long.valueOf(configs.get(Config.ProjectInvitationExpirationTime.key()))*1000;
         _allowUserToCreateProject = Boolean.valueOf(configs.get(Config.AllowUserToCreateProject.key()));
-        
-        
+
+
         // set up the email system for project invitations
 
         String smtpHost = configs.get("project.smtp.host");
@@ -153,13 +159,13 @@ public class ProjectManagerImpl implements ProjectManager, Manager{
 
         _emailInvite = new EmailInvite(smtpHost, smtpPort, useAuth, smtpUsername, smtpPassword, emailSender, smtpDebug);
         _executor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("Project-ExpireInvitations"));
-        
+
         return true;
     }
-    
+
     @Override
     public boolean start() {
-    	_executor.scheduleWithFixedDelay(new ExpiredInvitationsCleanup(), _projectCleanupExpInvInterval, _projectCleanupExpInvInterval, TimeUnit.SECONDS);
+	_executor.scheduleWithFixedDelay(new ExpiredInvitationsCleanup(), _projectCleanupExpInvInterval, _projectCleanupExpInvInterval, TimeUnit.SECONDS);
         return true;
     }
 
@@ -172,99 +178,99 @@ public class ProjectManagerImpl implements ProjectManager, Manager{
     public String getName() {
         return _name;
     }
-    
+
     @Override
     @ActionEvent(eventType = EventTypes.EVENT_PROJECT_CREATE, eventDescription = "creating project", create=true)
     @DB
     public Project createProject(String name, String displayText, String accountName, Long domainId) throws ResourceAllocationException{
         Account caller = UserContext.current().getCaller();
         Account owner = caller;
-        
+
         //check if the user authorized to create the project
         if (caller.getType() == Account.ACCOUNT_TYPE_NORMAL && !_allowUserToCreateProject) {
-        	throw new PermissionDeniedException("Regular user is not permitted to create a project");
+		throw new PermissionDeniedException("Regular user is not permitted to create a project");
         }
-        
+
         //Verify request parameters
         if ((accountName != null && domainId == null) || (domainId != null && accountName == null)) {
             throw new InvalidParameterValueException("Account name and domain id must be specified together");
         }
-        
+
         if (accountName != null) {
             owner = _accountMgr.finalizeOwner(caller, accountName, domainId, null);
         }
-        
+
         //don't allow 2 projects with the same name inside the same domain
         if (_projectDao.findByNameAndDomain(name, owner.getDomainId()) != null) {
             throw new InvalidParameterValueException("Project with name " + name + " already exists in domain id=" + owner.getDomainId());
         }
-        
+
         //do resource limit check
         _resourceLimitMgr.checkResourceLimit(owner, ResourceType.project);
-        
+
         Transaction txn = Transaction.currentTxn();
         txn.start();
-        
+
         //Create an account associated with the project
         StringBuilder acctNm = new StringBuilder("PrjAcct-");
         acctNm.append(name).append("-").append(owner.getDomainId());
-        
+
         Account projectAccount = _accountMgr.createAccount(acctNm.toString(), Account.ACCOUNT_TYPE_PROJECT, domainId, null, null);
-        
+
         Project project = _projectDao.persist(new ProjectVO(name, displayText, owner.getDomainId(), projectAccount.getId()));
-        
+
         //assign owner to the project
         assignAccountToProject(project, owner.getId(), ProjectAccount.Role.Admin);
-        
+
         if (project != null) {
             UserContext.current().setEventDetails("Project id=" + project.getId());
         }
-        
+
         //Increment resource count
         _resourceLimitMgr.incrementResourceCount(owner.getId(), ResourceType.project);
-        
+
         txn.commit();
-        
+
         return project;
     }
-    
-    
+
+
     @Override
     @ActionEvent(eventType = EventTypes.EVENT_PROJECT_CREATE, eventDescription = "creating project", async=true)
     @DB
     public Project enableProject(long projectId){
         Account caller = UserContext.current().getCaller();
-        
+
         ProjectVO project= getProject(projectId);
         //verify input parameters
         if (project == null) {
             throw new InvalidParameterValueException("Unable to find project by id " + projectId);
         }
-        
+
         _accountMgr.checkAccess(caller,AccessType.ModifyProject, true, _accountMgr.getAccount(project.getProjectAccountId()));
-        
+
         //at this point enabling project doesn't require anything, so just update the state
         project.setState(State.Active);
         _projectDao.update(projectId, project);
-        
+
         return project;
     }
-    
-    
+
+
     @Override
-    @ActionEvent(eventType = EventTypes.EVENT_PROJECT_DELETE, eventDescription = "deleting project", async = true) 
+    @ActionEvent(eventType = EventTypes.EVENT_PROJECT_DELETE, eventDescription = "deleting project", async = true)
     public boolean deleteProject(long projectId) {
         UserContext ctx = UserContext.current();
-        
+
         ProjectVO project= getProject(projectId);
         //verify input parameters
         if (project == null) {
             throw new InvalidParameterValueException("Unable to find project by id " + projectId);
         }
-        
+
         _accountMgr.checkAccess(ctx.getCaller(),AccessType.ModifyProject, true, _accountMgr.getAccount(project.getProjectAccountId()));
-        
-        return deleteProject(ctx.getCaller(), ctx.getCallerUserId(), project);  
+
+        return deleteProject(ctx.getCaller(), ctx.getCallerUserId(), project);
     }
 
     @DB
@@ -280,10 +286,10 @@ public class ProjectManagerImpl implements ProjectManager, Manager{
         Account projectOwner = getProjectOwner(project.getId());
         if (projectOwner != null) {
             _resourceLimitMgr.decrementResourceCount(projectOwner.getId(), ResourceType.project);
-        } 
-        
+        }
+
         txn.commit();
-        
+
         if (updateResult) {
             //pass system caller when clenaup projects account
             if (!cleanupProject(project, _accountDao.findById(Account.ACCOUNT_ID_SYSTEM), User.UID_SYSTEM)) {
@@ -297,31 +303,31 @@ public class ProjectManagerImpl implements ProjectManager, Manager{
             return false;
         }
     }
-    
+
     @DB
     private boolean cleanupProject(Project project, AccountVO caller, Long callerUserId) {
-        boolean result=true; 
+        boolean result=true;
         //Delete project's account
         AccountVO account = _accountDao.findById(project.getProjectAccountId());
         s_logger.debug("Deleting projects " + project + " internal account id=" + account.getId() + " as a part of project cleanup...");
-        
+
         result = result && _accountMgr.deleteAccount(account, callerUserId, caller);
-        
+
         if (result) {
             //Unassign all users from the project
-            
+
             Transaction txn = Transaction.currentTxn();
             txn.start();
-            
+
             s_logger.debug("Unassigning all accounts from project " + project + " as a part of project cleanup...");
             List<? extends ProjectAccount> projectAccounts = _projectAccountDao.listByProjectId(project.getId());
             for (ProjectAccount projectAccount : projectAccounts) {
                 result = result && unassignAccountFromProject(projectAccount.getProjectId(), projectAccount.getAccountId());
             }
-            
+
             s_logger.debug("Removing all invitations for the project " + project + " as a part of project cleanup...");
              _projectInvitationDao.cleanupInvitations(project.getId());
-            
+
             txn.commit();
             if (result) {
                 s_logger.debug("Accounts are unassign successfully from project " + project + " as a part of project cleanup...");
@@ -329,10 +335,10 @@ public class ProjectManagerImpl implements ProjectManager, Manager{
         } else {
             s_logger.warn("Failed to cleanup project's internal account");
         }
-        
+
         return result;
     }
-    
+
     @Override
     public boolean unassignAccountFromProject(long projectId, long accountId) {
         ProjectAccountVO projectAccount = _projectAccountDao.findByProjectIdAccountId(projectId, accountId);
@@ -340,7 +346,7 @@ public class ProjectManagerImpl implements ProjectManager, Manager{
             s_logger.debug("Account id=" + accountId + " is not assigned to project id=" + projectId + " so no need to unassign");
             return true;
         }
-        
+
         if ( _projectAccountDao.remove(projectAccount.getId())) {
             return true;
         } else {
@@ -348,23 +354,24 @@ public class ProjectManagerImpl implements ProjectManager, Manager{
             return false;
         }
     }
-    
+
     @Override
     public ProjectVO getProject (long projectId) {
         return _projectDao.findById(projectId);
     }
-    
+
     @Override
-    public Pair<List<? extends Project>, Integer> listProjects(Long id, String name, String displayText, String state, 
-            String accountName, Long domainId, String keyword, Long startIndex, Long pageSize, boolean listAll, 
+    public Pair<List<ProjectJoinVO>, Integer> listProjects(Long id, String name, String displayText, String state,
+            String accountName, Long domainId, String keyword, Long startIndex, Long pageSize, boolean listAll,
             boolean isRecursive, Map<String, String> tags) {
         Account caller = UserContext.current().getCaller();
         Long accountId = null;
         String path = null;
-        
-        Filter searchFilter = new Filter(ProjectVO.class, "id", false, startIndex, pageSize);
-        SearchBuilder<ProjectVO> sb = _projectDao.createSearchBuilder();
-        
+
+        Filter searchFilter = new Filter(ProjectJoinVO.class, "id", false, startIndex, pageSize);
+        SearchBuilder<ProjectJoinVO> sb = _projectJoinDao.createSearchBuilder();
+        sb.select(null, Func.DISTINCT, sb.entity().getId()); // select distinct ids
+
         if (_accountMgr.isAdmin(caller.getType())) {
             if (domainId != null) {
                 DomainVO domain = _domainDao.findById(domainId);
@@ -383,120 +390,125 @@ public class ProjectManagerImpl implements ProjectManager, Manager{
                 }
             }
             else { //domainId == null
-            	if (accountName != null) {            		
-                    throw new InvalidParameterValueException("could not find account " + accountName + " because domain is not specified");            		
-            	}
-            	
+		if (accountName != null) {
+                    throw new InvalidParameterValueException("could not find account " + accountName + " because domain is not specified");
+		}
+
             }
         } else {
             if (accountName != null && !accountName.equals(caller.getAccountName())) {
                 throw new PermissionDeniedException("Can't list account " + accountName + " projects; unauthorized");
             }
-            
+
             if (domainId != null && domainId.equals(caller.getDomainId())) {
                 throw new PermissionDeniedException("Can't list domain id= " + domainId + " projects; unauthorized");
             }
-            
+
             accountId = caller.getId();
         }
-        
-    	if (domainId == null && accountId == null && (caller.getType() == Account.ACCOUNT_TYPE_NORMAL || !listAll)) {
-    		accountId = caller.getId();
-    	} else if (caller.getType() == Account.ACCOUNT_TYPE_DOMAIN_ADMIN || (isRecursive && !listAll)) {
+
+	if (domainId == null && accountId == null && (caller.getType() == Account.ACCOUNT_TYPE_NORMAL || !listAll)) {
+		accountId = caller.getId();
+	} else if (caller.getType() == Account.ACCOUNT_TYPE_DOMAIN_ADMIN || (isRecursive && !listAll)) {
             DomainVO domain = _domainDao.findById(caller.getDomainId());
             path = domain.getPath();
         }
-        
+
         if (path != null) {
-            SearchBuilder<DomainVO> domainSearch = _domainDao.createSearchBuilder();
-            domainSearch.and("path", domainSearch.entity().getPath(), SearchCriteria.Op.LIKE);
-            sb.join("domainSearch", domainSearch, sb.entity().getDomainId(), domainSearch.entity().getId(), JoinBuilder.JoinType.INNER);
+            sb.and("domainPath", sb.entity().getDomainPath(), SearchCriteria.Op.LIKE);
         }
-        
+
         if (accountId != null) {
-            SearchBuilder<ProjectAccountVO> projectAccountSearch = _projectAccountDao.createSearchBuilder();
-            projectAccountSearch.and("accountId", projectAccountSearch.entity().getAccountId(), SearchCriteria.Op.EQ);
-            sb.join("projectAccountSearch", projectAccountSearch, sb.entity().getId(), projectAccountSearch.entity().getProjectId(), JoinBuilder.JoinType.INNER);
+            sb.and("accountId", sb.entity().getAccountId(), SearchCriteria.Op.EQ);
         }
-        
+
         if (tags != null && !tags.isEmpty()) {
-            SearchBuilder<ResourceTagVO> tagSearch = _resourceTagDao.createSearchBuilder();
-                for (int count=0; count < tags.size(); count++) {
-                    tagSearch.or().op("key" + String.valueOf(count), tagSearch.entity().getKey(), SearchCriteria.Op.EQ);
-                    tagSearch.and("value" + String.valueOf(count), tagSearch.entity().getValue(), SearchCriteria.Op.EQ);
-                    tagSearch.cp();
-                }
-                tagSearch.and("resourceType", tagSearch.entity().getResourceType(), SearchCriteria.Op.EQ);
-                sb.groupBy(sb.entity().getId());
-                sb.join("tagSearch", tagSearch, sb.entity().getId(), tagSearch.entity().getResourceId(), JoinBuilder.JoinType.INNER);
+            for (int count=0; count < tags.size(); count++) {
+                sb.or().op("key" + String.valueOf(count), sb.entity().getTagKey(), SearchCriteria.Op.EQ);
+                sb.and("value" + String.valueOf(count), sb.entity().getTagValue(), SearchCriteria.Op.EQ);
+                sb.cp();
+            }
         }
-        
-        SearchCriteria<ProjectVO> sc = sb.create();
-        
+
+
+        SearchCriteria<ProjectJoinVO> sc = sb.create();
+
         if (id != null) {
             sc.addAnd("id", Op.EQ, id);
         }
-        
+
         if (domainId != null && !isRecursive) {
             sc.addAnd("domainId", Op.EQ, domainId);
         }
-        
+
         if (name != null) {
             sc.addAnd("name", Op.EQ, name);
         }
-        
+
         if (displayText != null) {
             sc.addAnd("displayText", Op.EQ, displayText);
         }
-        
+
         if (accountId != null) {
-            sc.setJoinParameters("projectAccountSearch", "accountId", accountId);
+            sc.setParameters("accountId", accountId);
         }
-        
+
         if (state != null) {
             sc.addAnd("state", Op.EQ, state);
         }
-        
+
         if (keyword != null) {
-            SearchCriteria<ProjectVO> ssc = _projectDao.createSearchCriteria();
+            SearchCriteria<ProjectJoinVO> ssc = _projectJoinDao.createSearchCriteria();
             ssc.addOr("name", SearchCriteria.Op.LIKE, "%" + keyword + "%");
             ssc.addOr("displayText", SearchCriteria.Op.LIKE, "%" + keyword + "%");
             sc.addAnd("name", SearchCriteria.Op.SC, ssc);
         }
-        
+
         if (path != null) {
-            sc.setJoinParameters("domainSearch", "path", path);
+            sc.setParameters("domainPath", path);
         }
-        
+
         if (tags != null && !tags.isEmpty()) {
             int count = 0;
-            sc.setJoinParameters("tagSearch", "resourceType", TaggedResourceType.Project.toString());
-            for (String key : tags.keySet()) {
-                sc.setJoinParameters("tagSearch", "key" + String.valueOf(count), key);
-                sc.setJoinParameters("tagSearch", "value" + String.valueOf(count), tags.get(key));
+             for (String key : tags.keySet()) {
+                sc.setParameters("key" + String.valueOf(count), key);
+                sc.setParameters("value" + String.valueOf(count), tags.get(key));
                 count++;
             }
         }
 
-        Pair<List<ProjectVO>, Integer> result = _projectDao.searchAndCount(sc, searchFilter);
-        return new Pair<List<? extends Project>, Integer>(result.first(), result.second());
+        // search distinct projects to get count
+        Pair<List<ProjectJoinVO>, Integer> uniquePrjPair =  _projectJoinDao.searchAndCount(sc, searchFilter);
+        Integer count = uniquePrjPair.second();
+        if ( count.intValue() == 0 ){
+            // handle empty result cases
+            return uniquePrjPair;
+        }
+        List<ProjectJoinVO> uniquePrjs = uniquePrjPair.first();
+        Long[] prjIds = new Long[uniquePrjs.size()];
+        int i = 0;
+        for (ProjectJoinVO v : uniquePrjs ){
+            prjIds[i++] = v.getId();
+        }
+        List<ProjectJoinVO> prjs = _projectJoinDao.searchByIds(prjIds);
+        return new Pair<List<ProjectJoinVO>, Integer>(prjs, count);
     }
-    
+
     @Override
     public ProjectAccount assignAccountToProject(Project project, long accountId, ProjectAccount.Role accountRole) {
         return _projectAccountDao.persist(new ProjectAccountVO(project, accountId, accountRole));
     }
-    
+
     @Override @DB
     public boolean deleteAccountFromProject(long projectId, long accountId) {
         boolean success = true;
         Transaction txn = Transaction.currentTxn();
         txn.start();
-        
+
         //remove account
         ProjectAccountVO projectAccount = _projectAccountDao.findByProjectIdAccountId(projectId, accountId);
         success = _projectAccountDao.remove(projectAccount.getId());
-        
+
         //remove all invitations for account
         if (success) {
             s_logger.debug("Removed account " + accountId + " from project " + projectId + " , cleaning up old invitations for account/project...");
@@ -505,36 +517,36 @@ public class ProjectManagerImpl implements ProjectManager, Manager{
                 success = success && _projectInvitationDao.remove(invite.getId());
             }
         }
-        
+
         txn.commit();
         return success;
     }
-    
+
     @Override
     public Account getProjectOwner(long projectId) {
         ProjectAccount prAcct = _projectAccountDao.getProjectOwner(projectId);
         if (prAcct != null) {
             return _accountMgr.getAccount(prAcct.getAccountId());
         }
-        
+
         return null;
     }
-    
+
     @Override
     public ProjectVO findByProjectAccountId(long projectAccountId) {
         return _projectDao.findByProjectAccountId(projectAccountId);
     }
-    
+
     @Override
     public ProjectVO findByProjectAccountIdIncludingRemoved(long projectAccountId) {
         return _projectDao.findByProjectAccountIdIncludingRemoved(projectAccountId);
     }
-    
+
     @Override
     public Project findByNameAndDomainId(String name, long domainId) {
         return _projectDao.findByNameAndDomain(name, domainId);
     }
-    
+
     @Override
     public boolean canAccessProjectAccount(Account caller, long accountId) {
         //ROOT admin always can access the project
@@ -545,10 +557,10 @@ public class ProjectManagerImpl implements ProjectManager, Manager{
             _accountMgr.checkAccess(caller, _domainDao.findById(owner.getDomainId()));
             return true;
         }
-        
+
         return _projectAccountDao.canAccessProjectAccount(caller.getId(), accountId);
     }
-    
+
     public boolean canModifyProjectAccount(Account caller, long accountId) {
         //ROOT admin always can access the project
         if (caller.getType() == Account.ACCOUNT_TYPE_ADMIN) {
@@ -560,29 +572,29 @@ public class ProjectManagerImpl implements ProjectManager, Manager{
         }
         return _projectAccountDao.canModifyProjectAccount(caller.getId(), accountId);
     }
-    
+
     @Override @DB
     @ActionEvent(eventType = EventTypes.EVENT_PROJECT_UPDATE, eventDescription = "updating project", async=true)
     public Project updateProject(long projectId, String displayText, String newOwnerName) throws ResourceAllocationException{
         Account caller = UserContext.current().getCaller();
-        
+
         //check that the project exists
         ProjectVO project = getProject(projectId);
-        
+
         if (project == null) {
             throw new InvalidParameterValueException("Unable to find the project id=" + projectId);
         }
-       
+
         //verify permissions
         _accountMgr.checkAccess(caller,AccessType.ModifyProject, true, _accountMgr.getAccount(project.getProjectAccountId()));
-        
+
         Transaction txn = Transaction.currentTxn();
         txn.start();
         if (displayText != null) {
             project.setDisplayText(displayText);
             _projectDao.update(projectId, project);
         }
-        
+
         if (newOwnerName != null) {
             //check that the new owner exists
             Account futureOwnerAccount = _accountMgr.getActiveAccountByName(newOwnerName, project.getDomainId());
@@ -595,68 +607,68 @@ public class ProjectManagerImpl implements ProjectManager, Manager{
                 if (futureOwner == null) {
                     throw new InvalidParameterValueException("Account " + newOwnerName + " doesn't belong to the project. Add it to the project first and then change the project's ownership");
                 }
-                
+
                 //do resource limit check
                 _resourceLimitMgr.checkResourceLimit(_accountMgr.getAccount(futureOwnerAccount.getId()), ResourceType.project);
-                
+
                 //unset the role for the old owner
                 ProjectAccountVO currentOwner = _projectAccountDao.findByProjectIdAccountId(projectId, currentOwnerAccount.getId());
                 currentOwner.setAccountRole(Role.Regular);
                 _projectAccountDao.update(currentOwner.getId(), currentOwner);
                 _resourceLimitMgr.decrementResourceCount(currentOwnerAccount.getId(), ResourceType.project);
-                
+
                 //set new owner
                 futureOwner.setAccountRole(Role.Admin);
                 _projectAccountDao.update(futureOwner.getId(), futureOwner);
                 _resourceLimitMgr.incrementResourceCount(futureOwnerAccount.getId(), ResourceType.project);
 
-                
+
             } else {
                 s_logger.trace("Future owner " + newOwnerName + "is already the owner of the project id=" + projectId);
             }
         }
-        
+
         txn.commit();
-        
+
         return _projectDao.findById(projectId);
-        
+
     }
-    
+
     @Override
     @ActionEvent(eventType = EventTypes.EVENT_PROJECT_ACCOUNT_ADD, eventDescription = "adding account to project", async=true)
     public boolean addAccountToProject(long projectId, String accountName, String email) {
         Account caller = UserContext.current().getCaller();
-        
+
         //check that the project exists
         Project project = getProject(projectId);
-        
+
         if (project == null) {
-        	InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find project with specified id");
-        	ex.addProxyObject(project, projectId, "projectId");            
+		InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find project with specified id");
+		ex.addProxyObject(project, projectId, "projectId");
             throw ex;
         }
-        
+
         //User can be added to Active project only
         if (project.getState() != Project.State.Active) {
-        	InvalidParameterValueException ex = new InvalidParameterValueException("Can't add account to the specified project id in state=" + project.getState() + " as it's no longer active");
-        	ex.addProxyObject(project, projectId, "projectId");
+		InvalidParameterValueException ex = new InvalidParameterValueException("Can't add account to the specified project id in state=" + project.getState() + " as it's no longer active");
+		ex.addProxyObject(project, projectId, "projectId");
             throw ex;
         }
-       
+
         //check that account-to-add exists
         Account account = null;
         if (accountName != null) {
             account = _accountMgr.getActiveAccountByName(accountName, project.getDomainId());
             if (account == null) {
-            	InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find account name=" + accountName + " in specified domain id");
-                // We don't have a DomainVO object with us, so just pass the tablename "domain" manually.                
+		InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find account name=" + accountName + " in specified domain id");
+                // We don't have a DomainVO object with us, so just pass the tablename "domain" manually.
                 ex.addProxyObject("domain", project.getDomainId(), "domainId");
                 throw ex;
             }
-            
+
             //verify permissions - only project owner can assign
             _accountMgr.checkAccess(caller, AccessType.ModifyProject, true, _accountMgr.getAccount(project.getProjectAccountId()));
-            
+
             //Check if the account already added to the project
             ProjectAccount projectAccount =  _projectAccountDao.findByProjectIdAccountId(projectId, account.getId());
             if (projectAccount != null) {
@@ -664,7 +676,7 @@ public class ProjectManagerImpl implements ProjectManager, Manager{
                 return true;
             }
         }
-        
+
         if (_invitationRequired) {
             return inviteAccountToProject(project, account, email);
         } else {
@@ -679,7 +691,7 @@ public class ProjectManagerImpl implements ProjectManager, Manager{
             }
         }
     }
-    
+
     private boolean inviteAccountToProject(Project project, Account account, String email) {
         if (account != null) {
             if (createAccountInvitation(project, account.getId()) != null) {
@@ -687,9 +699,9 @@ public class ProjectManagerImpl implements ProjectManager, Manager{
             } else {
                 s_logger.warn("Failed to generate invitation for account " + account.getAccountName() + " to project id=" + project);
                 return false;
-            } 
+            }
         }
-      
+
         if (email != null) {
             //generate the token
             String token = generateToken(10);
@@ -698,93 +710,93 @@ public class ProjectManagerImpl implements ProjectManager, Manager{
             } else {
               s_logger.warn("Failed to generate invitation for email " + email + " to project id=" + project);
               return false;
-            } 
+            }
         }
-        
+
         return false;
     }
-    
+
     @Override
     @ActionEvent(eventType = EventTypes.EVENT_PROJECT_ACCOUNT_REMOVE, eventDescription = "removing account from project", async=true)
     public boolean deleteAccountFromProject(long projectId, String accountName) {
         Account caller = UserContext.current().getCaller();
-        
+
         //check that the project exists
         Project project = getProject(projectId);
-        
+
         if (project == null) {
-        	InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find project with specified id");
-        	ex.addProxyObject(project, projectId, "projectId");            
+		InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find project with specified id");
+		ex.addProxyObject(project, projectId, "projectId");
             throw ex;
         }
-       
+
         //check that account-to-remove exists
         Account account = _accountMgr.getActiveAccountByName(accountName, project.getDomainId());
         if (account == null) {
-        	InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find account name=" + accountName + " in domain id=" + project.getDomainId());
+		InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find account name=" + accountName + " in domain id=" + project.getDomainId());
             // Since we don't have a domainVO object, pass the table name manually.
-            ex.addProxyObject("domain", project.getDomainId(), "domainId");           
+            ex.addProxyObject("domain", project.getDomainId(), "domainId");
         }
-        
+
         //verify permissions
         _accountMgr.checkAccess(caller,AccessType.ModifyProject, true, _accountMgr.getAccount(project.getProjectAccountId()));
-        
+
         //Check if the account exists in the project
         ProjectAccount projectAccount =  _projectAccountDao.findByProjectIdAccountId(projectId, account.getId());
         if (projectAccount == null) {
         	InvalidParameterValueException ex = new InvalidParameterValueException("Account " + accountName + " is not assigned to the project with specified id");
         	// Use the projectVO object and not the projectAccount object to inject the projectId.
-        	ex.addProxyObject(project, projectId, "projectId");
+		ex.addProxyObject(project, projectId, "projectId");
             throw ex;
         }
-        
+
         //can't remove the owner of the project
         if (projectAccount.getAccountRole() == Role.Admin) {
-        	InvalidParameterValueException ex = new InvalidParameterValueException("Unable to delete account " + accountName + " from the project with specified id as the account is the owner of the project");
-        	ex.addProxyObject(project, projectId, "projectId");
+		InvalidParameterValueException ex = new InvalidParameterValueException("Unable to delete account " + accountName + " from the project with specified id as the account is the owner of the project");
+		ex.addProxyObject(project, projectId, "projectId");
             throw ex;
         }
-        
+
         return deleteAccountFromProject(projectId, account.getId());
     }
-    
-    
+
+
     @Override
     public Pair<List<? extends ProjectAccount>, Integer> listProjectAccounts(long projectId, String accountName, String role, Long startIndex, Long pageSizeVal) {
         Account caller = UserContext.current().getCaller();
-        
+
         //check that the project exists
         Project project = getProject(projectId);
-        
+
         if (project == null) {
             throw new InvalidParameterValueException("Unable to find the project id=" + projectId);
         }
-        
+
         //verify permissions - only accounts belonging to the project can list project's account
         if (!_accountMgr.isAdmin(caller.getType()) && _projectAccountDao.findByProjectIdAccountId(projectId, caller.getAccountId()) == null) {
             throw new PermissionDeniedException("Account " + caller + " is not authorized to list users of the project id=" + projectId);
         }
-        
+
         Filter searchFilter = new Filter(ProjectAccountVO.class, "id", false, startIndex, pageSizeVal);
         SearchBuilder<ProjectAccountVO> sb = _projectAccountDao.createSearchBuilder();
         sb.and("accountRole", sb.entity().getAccountRole(), Op.EQ);
         sb.and("projectId", sb.entity().getProjectId(), Op.EQ);
-        
+
         SearchBuilder<AccountVO> accountSearch;
         if (accountName != null) {
             accountSearch = _accountDao.createSearchBuilder();
             accountSearch.and("accountName", accountSearch.entity().getAccountName(), SearchCriteria.Op.EQ);
             sb.join("accountSearch", accountSearch, sb.entity().getAccountId(), accountSearch.entity().getId(), JoinBuilder.JoinType.INNER);
         }
-        
+
         SearchCriteria<ProjectAccountVO> sc = sb.create();
-        
+
         sc.setParameters("projectId", projectId);
-        
+
         if (role != null) {
             sc.setParameters("accountRole", role);
         }
-        
+
         if (accountName != null) {
             sc.setJoinParameters("accountSearch", "accountName", accountName);
         }
@@ -792,14 +804,14 @@ public class ProjectManagerImpl implements ProjectManager, Manager{
         Pair<List<ProjectAccountVO>, Integer> result = _projectAccountDao.searchAndCount(sc, searchFilter);
         return new Pair<List<? extends ProjectAccount>, Integer>(result.first(), result.second());
     }
-    
-    public ProjectInvitation createAccountInvitation(Project project, Long accountId) { 
+
+    public ProjectInvitation createAccountInvitation(Project project, Long accountId) {
         if (activeInviteExists(project, accountId, null)) {
             throw new InvalidParameterValueException("There is already a pending invitation for account id=" + accountId + " to the project id=" + project);
         }
-        
+
         ProjectInvitation invitation= _projectInvitationDao.persist(new ProjectInvitationVO(project.getId(), accountId, project.getDomainId(), null, null));
-        
+
         return invitation;
     }
 
@@ -811,14 +823,14 @@ public class ProjectManagerImpl implements ProjectManager, Manager{
     	ProjectInvitationVO invite = null;
     	if (accountId != null) {
     		invite = _projectInvitationDao.findByAccountIdProjectId(accountId, project.getId());
-    	} else if (email != null) {
-    		 invite = _projectInvitationDao.findByEmailAndProjectId(email, project.getId());
-    	}
-    	
+	} else if (email != null) {
+		 invite = _projectInvitationDao.findByEmailAndProjectId(email, project.getId());
+	}
+
         if (invite != null) {
-            if (invite.getState() == ProjectInvitation.State.Completed || 
+            if (invite.getState() == ProjectInvitation.State.Completed ||
                     (invite.getState() == ProjectInvitation.State.Pending && _projectInvitationDao.isActive(invite.getId(), _invitationTimeOut))) {
-            	return true;
+		return true;
             } else {
                 if (invite.getState() == ProjectInvitation.State.Pending) {
                     expireInvitation(invite);
@@ -836,13 +848,13 @@ public class ProjectManagerImpl implements ProjectManager, Manager{
         txn.commit();
         return false;
 	}
-    
+
     public ProjectInvitation generateTokenBasedInvitation(Project project, String email, String token) {
         //verify if the invitation was already generated
-    	 if (activeInviteExists(project, null, email)) {
+	 if (activeInviteExists(project, null, email)) {
              throw new InvalidParameterValueException("There is already a pending invitation for email " + email + " to the project id=" + project);
          }
-        
+
         ProjectInvitation projectInvitation = _projectInvitationDao.persist(new ProjectInvitationVO(project.getId(), null, project.getDomainId(), email, token));
         try {
             _emailInvite.sendInvite(token, email, project.getId());
@@ -851,28 +863,28 @@ public class ProjectManagerImpl implements ProjectManager, Manager{
             _projectInvitationDao.remove(projectInvitation.getId());
             return null;
         }
-        
+
         return projectInvitation;
     }
-    
+
     private boolean expireInvitation(ProjectInvitationVO invite) {
         s_logger.debug("Expiring invitation id=" + invite.getId());
         invite.setState(ProjectInvitation.State.Expired);
         return _projectInvitationDao.update(invite.getId(), invite);
     }
-    
+
     @Override
     public Pair<List<? extends ProjectInvitation>, Integer> listProjectInvitations(Long id, Long projectId,
             String accountName, Long domainId, String state, boolean activeOnly, Long startIndex, Long pageSizeVal, boolean isRecursive, boolean listAll) {
         Account caller = UserContext.current().getCaller();
         List<Long> permittedAccounts = new ArrayList<Long>();
-        
+
         Ternary<Long, Boolean, ListProjectResourcesCriteria> domainIdRecursiveListProject = new Ternary<Long, Boolean, ListProjectResourcesCriteria>(domainId, isRecursive, null);
         _accountMgr.buildACLSearchParameters(caller, id, accountName, projectId, permittedAccounts, domainIdRecursiveListProject, listAll, true);
         domainId = domainIdRecursiveListProject.first();
         isRecursive = domainIdRecursiveListProject.second();
-        ListProjectResourcesCriteria listProjectResourcesCriteria = domainIdRecursiveListProject.third();        
-        
+        ListProjectResourcesCriteria listProjectResourcesCriteria = domainIdRecursiveListProject.third();
+
         Filter searchFilter = new Filter(ProjectInvitationVO.class, "id", true, startIndex, pageSizeVal);
         SearchBuilder<ProjectInvitationVO> sb = _projectInvitationDao.createSearchBuilder();
         _accountMgr.buildACLSearchBuilder(sb, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria);
@@ -885,19 +897,19 @@ public class ProjectManagerImpl implements ProjectManager, Manager{
         SearchCriteria<ProjectInvitationVO> sc = sb.create();
         _accountMgr.buildACLSearchCriteria(sc, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria);
 
-        
+
         if (projectId != null){
             sc.setParameters("projectId", projectId);
         }
-        
+
         if (state != null) {
             sc.setParameters("state", state);
         }
-        
+
         if (id != null) {
             sc.setParameters("id", id);
         }
-        
+
         if (activeOnly) {
             sc.setParameters("state", ProjectInvitation.State.Pending);
             sc.setParameters("created", new Date((DateUtil.currentGMTTime().getTime()) - _invitationTimeOut));
@@ -906,41 +918,41 @@ public class ProjectManagerImpl implements ProjectManager, Manager{
         Pair<List<ProjectInvitationVO>, Integer> result = _projectInvitationDao.searchAndCount(sc, searchFilter);
         return new Pair<List<? extends ProjectInvitation>, Integer>(result.first(), result.second());
     }
-    
+
     @Override @DB
     @ActionEvent(eventType = EventTypes.EVENT_PROJECT_INVITATION_UPDATE, eventDescription = "updating project invitation", async=true)
     public boolean updateInvitation(long projectId, String accountName, String token, boolean accept) {
         Account caller = UserContext.current().getCaller();
         Long accountId = null;
         boolean result = true;
-        
+
         //if accountname and token are null, default accountname to caller's account name
         if (accountName == null && token == null) {
             accountName = caller.getAccountName();
         }
-        
+
         //check that the project exists
         Project project = getProject(projectId);
-        
+
         if (project == null) {
             throw new InvalidParameterValueException("Unable to find the project id=" + projectId);
         }
-        
+
         if (accountName != null) {
             //check that account-to-remove exists
             Account account = _accountMgr.getActiveAccountByName(accountName, project.getDomainId());
             if (account == null) {
                 throw new InvalidParameterValueException("Unable to find account name=" + accountName + " in domain id=" + project.getDomainId());
             }
-            
+
             //verify permissions
             _accountMgr.checkAccess(caller, null, true, account);
-            
+
             accountId = account.getId();
         } else {
             accountId = caller.getId();
         }
-        
+
         //check that invitation exists
         ProjectInvitationVO invite = null;
         if (token == null) {
@@ -948,7 +960,7 @@ public class ProjectManagerImpl implements ProjectManager, Manager{
         } else {
             invite = _projectInvitationDao.findPendingByTokenAndProjectId(token, projectId, ProjectInvitation.State.Pending);
         }
-        
+
         if (invite != null) {
             if (!_projectInvitationDao.isActive(invite.getId(), _invitationTimeOut) && accept) {
                 expireInvitation(invite);
@@ -956,116 +968,116 @@ public class ProjectManagerImpl implements ProjectManager, Manager{
             } else {
                 Transaction txn = Transaction.currentTxn();
                 txn.start();
-                
+
                 ProjectInvitation.State newState = accept ? ProjectInvitation.State.Completed : ProjectInvitation.State.Declined;
-                
+
                //update invitation
                s_logger.debug("Marking invitation " + invite + " with state " + newState);
                invite.setState(newState);
                result = _projectInvitationDao.update(invite.getId(), invite);
-               
+
                if (result && accept) {
                    //check if account already exists for the project (was added before invitation got accepted)
                    ProjectAccount projectAccount =  _projectAccountDao.findByProjectIdAccountId(projectId, accountId);
                    if (projectAccount != null) {
                        s_logger.debug("Account " + accountName + " already added to the project id=" + projectId);
                    } else {
-                       assignAccountToProject(project, accountId, ProjectAccount.Role.Regular); 
+                       assignAccountToProject(project, accountId, ProjectAccount.Role.Regular);
                    }
                } else {
                    s_logger.warn("Failed to update project invitation " + invite + " with state " + newState);
                }
-              
+
                txn.commit();
             }
         } else {
             throw new InvalidParameterValueException("Unable to find invitation for account name=" + accountName + " to the project id=" + projectId);
         }
-        
+
         return result;
     }
-    
+
     @Override
     public List<Long> listPermittedProjectAccounts(long accountId) {
         return _projectAccountDao.listPermittedAccountIds(accountId);
     }
-    
+
     @Override
     @ActionEvent(eventType = EventTypes.EVENT_PROJECT_ACTIVATE, eventDescription = "activating project")
     @DB
     public Project activateProject(long projectId) {
         Account caller = UserContext.current().getCaller();
-        
+
         //check that the project exists
         ProjectVO project = getProject(projectId);
-        
+
         if (project == null) {
-        	InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find project with specified id");
-        	ex.addProxyObject(project, projectId, "projectId");
+		InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find project with specified id");
+		ex.addProxyObject(project, projectId, "projectId");
             throw ex;
         }
-       
+
         //verify permissions
         _accountMgr.checkAccess(caller,AccessType.ModifyProject, true, _accountMgr.getAccount(project.getProjectAccountId()));
-        
+
         //allow project activation only when it's in Suspended state
         Project.State currentState = project.getState();
-        
+
         if (currentState == State.Active) {
             s_logger.debug("The project id=" + projectId + " is already active, no need to activate it again");
             return project;
-        } 
-        
+        }
+
         if (currentState != State.Suspended) {
             throw new InvalidParameterValueException("Can't activate the project in " + currentState + " state");
         }
-        
+
         Transaction txn = Transaction.currentTxn();
         txn.start();
-        
+
         project.setState(Project.State.Active);
         _projectDao.update(projectId, project);
-        
+
         _accountMgr.enableAccount(project.getProjectAccountId());
-        
+
         txn.commit();
-        
+
         return _projectDao.findById(projectId);
     }
-    
-    
+
+
     @Override
     @ActionEvent(eventType = EventTypes.EVENT_PROJECT_SUSPEND, eventDescription = "suspending project", async = true)
     public Project suspendProject (long projectId) throws ConcurrentOperationException, ResourceUnavailableException {
         Account caller = UserContext.current().getCaller();
-        
+
         ProjectVO project= getProject(projectId);
         //verify input parameters
         if (project == null) {
         	InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find project with specified id");
-        	ex.addProxyObject(project, projectId, "projectId");
+		ex.addProxyObject(project, projectId, "projectId");
             throw ex;
         }
-        
+
         _accountMgr.checkAccess(caller,AccessType.ModifyProject, true, _accountMgr.getAccount(project.getProjectAccountId()));
-        
+
         if (suspendProject(project)) {
             s_logger.debug("Successfully suspended project id=" + projectId);
             return _projectDao.findById(projectId);
         } else {
         	CloudRuntimeException ex = new CloudRuntimeException("Failed to suspend project with specified id");
-        	ex.addProxyObject(project, projectId, "projectId");
+		ex.addProxyObject(project, projectId, "projectId");
             throw ex;
         }
-        
+
     }
-    
+
     private boolean suspendProject(ProjectVO project) throws ConcurrentOperationException, ResourceUnavailableException {
-    	
+
         s_logger.debug("Marking project " + project + " with state " + State.Suspended + " as a part of project suspend...");
         project.setState(State.Suspended);
         boolean updateResult = _projectDao.update(project.getId(), project);
-        
+
         if (updateResult) {
             long projectAccountId = project.getProjectAccountId();
             if (!_accountMgr.disableAccount(projectAccountId)) {
@@ -1076,8 +1088,8 @@ public class ProjectManagerImpl implements ProjectManager, Manager{
         }
         return true;
     }
-    
-    
+
+
     public static String generateToken(int length) {
         String charset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
         Random rand = new Random(System.currentTimeMillis());
@@ -1088,7 +1100,7 @@ public class ProjectManagerImpl implements ProjectManager, Manager{
         }
         return sb.toString();
     }
-    
+
     class EmailInvite {
         private Session _smtpSession;
         private final String _smtpHost;
@@ -1138,7 +1150,7 @@ public class ProjectManagerImpl implements ProjectManager, Manager{
             }
         }
 
-        public void sendInvite(String token, String email, long projectId) throws MessagingException, UnsupportedEncodingException {  
+        public void sendInvite(String token, String email, long projectId) throws MessagingException, UnsupportedEncodingException {
             if (_smtpSession != null) {
                 InternetAddress address = null;
                 if (email != null) {
@@ -1148,9 +1160,9 @@ public class ProjectManagerImpl implements ProjectManager, Manager{
                         s_logger.error("Exception creating address for: " + email, ex);
                     }
                 }
-                
+
                 String content = "You've been invited to join the CloudStack project id=" + projectId + ". Please use token " + token + " to complete registration";
-                
+
                 SMTPMessage msg = new SMTPMessage(_smtpSession);
                 msg.setSender(new InternetAddress(_emailSender, _emailSender));
                 msg.setFrom(new InternetAddress(_emailSender, _emailSender));
@@ -1174,36 +1186,36 @@ public class ProjectManagerImpl implements ProjectManager, Manager{
             }
         }
     }
-    
-    
+
+
     @Override @DB
     @ActionEvent(eventType = EventTypes.EVENT_PROJECT_INVITATION_REMOVE, eventDescription = "removing project invitation", async=true)
     public boolean deleteProjectInvitation(long id) {
         Account caller = UserContext.current().getCaller();
-        
+
         ProjectInvitation invitation = _projectInvitationDao.findById(id);
         if (invitation == null) {
             throw new InvalidParameterValueException("Unable to find project invitation by id " + id);
         }
-        
+
         //check that the project exists
         Project project = getProject(invitation.getProjectId());
-        
+
         //check permissions - only project owner can remove the invitations
         _accountMgr.checkAccess(caller, AccessType.ModifyProject, true, _accountMgr.getAccount(project.getProjectAccountId()));
-        
+
         if (_projectInvitationDao.remove(id)) {
             s_logger.debug("Project Invitation id=" + id + " is removed");
             return true;
         } else {
             s_logger.debug("Failed to remove project invitation id=" + id);
-            return false; 
+            return false;
         }
     }
-    
+
     public class ExpiredInvitationsCleanup implements Runnable {
-    	@Override
-    	public void run() {
+	@Override
+	public void run() {
     		try {
     			TimeZone.getDefault();
     			List<ProjectInvitationVO> invitationsToExpire = _projectInvitationDao.listInvitationsToExpire(_invitationTimeOut);
@@ -1228,7 +1240,7 @@ public class ProjectManagerImpl implements ProjectManager, Manager{
 
     @Override
     public boolean allowUserToCreateProject() {
-    	return _allowUserToCreateProject;
+	return _allowUserToCreateProject;
     }
-    
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/8735716b/server/src/com/cloud/projects/dao/ProjectJoinDao.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/projects/dao/ProjectJoinDao.java b/server/src/com/cloud/projects/dao/ProjectJoinDao.java
new file mode 100644
index 0000000..c5cc422
--- /dev/null
+++ b/server/src/com/cloud/projects/dao/ProjectJoinDao.java
@@ -0,0 +1,36 @@
+// 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 com.cloud.projects.dao;
+
+import java.util.List;
+
+import org.apache.cloudstack.api.response.ProjectResponse;
+import org.apache.cloudstack.api.view.vo.ProjectJoinVO;
+import com.cloud.projects.Project;
+import com.cloud.utils.db.GenericDao;
+
+public interface ProjectJoinDao extends GenericDao<ProjectJoinVO, Long> {
+
+    ProjectResponse newProjectResponse(ProjectJoinVO proj);
+
+    ProjectResponse setProjectResponse(ProjectResponse rsp, ProjectJoinVO proj);
+
+    List<ProjectJoinVO> newProjectView(Project proj);
+
+    List<ProjectJoinVO> searchByIds(Long... ids);
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/8735716b/server/src/com/cloud/projects/dao/ProjectJoinDaoImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/projects/dao/ProjectJoinDaoImpl.java b/server/src/com/cloud/projects/dao/ProjectJoinDaoImpl.java
new file mode 100644
index 0000000..dc6ba86
--- /dev/null
+++ b/server/src/com/cloud/projects/dao/ProjectJoinDaoImpl.java
@@ -0,0 +1,130 @@
+// 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 com.cloud.projects.dao;
+
+import java.util.List;
+
+import javax.ejb.Local;
+
+import org.apache.log4j.Logger;
+
+import com.cloud.api.ApiDBUtils;
+import org.apache.cloudstack.api.response.ProjectResponse;
+import org.apache.cloudstack.api.view.vo.ProjectJoinVO;
+import org.apache.cloudstack.api.view.vo.ResourceTagJoinVO;
+import com.cloud.projects.Project;
+import com.cloud.utils.db.GenericDaoBase;
+import com.cloud.utils.db.SearchBuilder;
+import com.cloud.utils.db.SearchCriteria;
+
+
+@Local(value={ProjectJoinDao.class})
+public class ProjectJoinDaoImpl extends GenericDaoBase<ProjectJoinVO, Long> implements ProjectJoinDao {
+    public static final Logger s_logger = Logger.getLogger(ProjectJoinDaoImpl.class);
+
+    private SearchBuilder<ProjectJoinVO> vrSearch;
+
+    private SearchBuilder<ProjectJoinVO> vrIdSearch;
+
+
+    protected ProjectJoinDaoImpl() {
+
+        vrSearch = createSearchBuilder();
+        vrSearch.and("idIN", vrSearch.entity().getId(), SearchCriteria.Op.IN);
+        vrSearch.done();
+
+        vrIdSearch = createSearchBuilder();
+        vrIdSearch.and("id", vrIdSearch.entity().getId(), SearchCriteria.Op.EQ);
+        vrIdSearch.done();
+
+        this._count = "select count(distinct id) from project_view WHERE ";
+    }
+
+
+
+
+
+
+    @Override
+    public ProjectResponse newProjectResponse(ProjectJoinVO proj) {
+        ProjectResponse response = new ProjectResponse();
+        response.setId(proj.getUuid());
+        response.setName(proj.getName());
+        response.setDisplaytext(proj.getDisplayText());
+        response.setState(proj.getState().toString());
+
+        response.setDomainId(proj.getDomainUuid());
+        response.setDomain(proj.getDomainName());
+
+        response.setOwner(proj.getOwner());
+
+        // update tag information
+        Long tag_id = proj.getTagId();
+        if (tag_id != null && tag_id.longValue() > 0) {
+            ResourceTagJoinVO vtag = ApiDBUtils.findResourceTagViewById(tag_id);
+            if ( vtag != null ){
+                response.addTag(ApiDBUtils.newResourceTagResponse(vtag, false));
+            }
+        }
+
+        response.setObjectName("project");
+        return response;
+    }
+
+
+
+    @Override
+    public ProjectResponse setProjectResponse(ProjectResponse rsp, ProjectJoinVO proj) {
+        // update tag information
+        Long tag_id = proj.getTagId();
+        if (tag_id != null && tag_id.longValue() > 0) {
+            ResourceTagJoinVO vtag = ApiDBUtils.findResourceTagViewById(tag_id);
+            if ( vtag != null ){
+                rsp.addTag(ApiDBUtils.newResourceTagResponse(vtag, false));
+            }
+        }
+        return rsp;
+    }
+
+
+
+
+
+
+    @Override
+    public List<ProjectJoinVO> newProjectView(Project proj) {
+        SearchCriteria<ProjectJoinVO> sc = vrIdSearch.create();
+        sc.setParameters("id", proj.getId());
+        return searchIncludingRemoved(sc, null, null, false);
+    }
+
+
+
+
+
+
+    @Override
+    public List<ProjectJoinVO> searchByIds(Long... ids) {
+        SearchCriteria<ProjectJoinVO> sc = vrSearch.create();
+        sc.setParameters("idIN", ids);
+        return searchIncludingRemoved(sc, null, null, false);
+    }
+
+
+
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/8735716b/server/test/com/cloud/projects/MockProjectManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/test/com/cloud/projects/MockProjectManagerImpl.java b/server/test/com/cloud/projects/MockProjectManagerImpl.java
index d8bce2f..dad777a 100644
--- a/server/test/com/cloud/projects/MockProjectManagerImpl.java
+++ b/server/test/com/cloud/projects/MockProjectManagerImpl.java
@@ -22,6 +22,8 @@ import java.util.Map;
 import javax.ejb.Local;
 import javax.naming.ConfigurationException;
 
+import org.apache.cloudstack.api.view.vo.ProjectJoinVO;
+
 import com.cloud.exception.ConcurrentOperationException;
 import com.cloud.exception.ResourceAllocationException;
 import com.cloud.exception.ResourceUnavailableException;
@@ -225,12 +227,12 @@ public class MockProjectManagerImpl implements ProjectManager, Manager {
      * @see com.cloud.projects.ProjectService#listProjects(java.lang.Long, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.Long, java.lang.String, java.lang.Long, java.lang.Long, boolean, boolean, java.util.Map)
      */
     @Override
-    public Pair<List<? extends Project>, Integer> listProjects(Long id, String name, String displayText, String state, String accountName, Long domainId, String keyword, Long startIndex, Long pageSize, boolean listAll,
+    public Pair<List<ProjectJoinVO>, Integer> listProjects(Long id, String name, String displayText, String state, String accountName, Long domainId, String keyword, Long startIndex, Long pageSize, boolean listAll,
             boolean isRecursive, Map<String, String> tags) {
         // TODO Auto-generated method stub
         return null;
     }
-    
+
     @Override
     public Project findByProjectAccountIdIncludingRemoved(long projectAccountId) {
         return null;

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/8735716b/setup/db/create-schema.sql
----------------------------------------------------------------------
diff --git a/setup/db/create-schema.sql b/setup/db/create-schema.sql
index 722b31f..6699d76 100755
--- a/setup/db/create-schema.sql
+++ b/setup/db/create-schema.sql
@@ -2890,3 +2890,36 @@ domain.path domain_path
 from user
 inner join account on user.account_id = account.id
 inner join domain on account.domain_id=domain.id;
+
+DROP VIEW IF EXISTS `cloud`.`project_view`;
+CREATE VIEW project_view AS
+select
+projects.id,
+projects.uuid,
+projects.name,
+projects.display_text,
+projects.state,
+projects.removed,
+projects.created,
+account.account_name owner,
+pacct.account_id,
+domain.id domain_id,
+domain.uuid domain_uuid,
+domain.name domain_name,
+domain.path domain_path,
+resource_tags.id tag_id,
+resource_tags.uuid tag_uuid,
+resource_tags.key tag_key,
+resource_tags.value tag_value,
+resource_tags.domain_id tag_domain_id,
+resource_tags.account_id tag_account_id,
+resource_tags.resource_id tag_resource_id,
+resource_tags.resource_uuid tag_resource_uuid,
+resource_tags.resource_type tag_resource_type,
+resource_tags.customer tag_customer
+from projects
+inner join domain on projects.domain_id=domain.id
+inner join project_account on projects.id = project_account.project_id and project_account.account_role = "Admin"
+inner join account on account.id = project_account.account_id
+left join resource_tags on resource_tags.resource_id = projects.id and resource_tags.resource_type = "Project"
+left join project_account pacct on projects.id = pacct.project_id;