/* * JBoss, Home of Professional Open Source. * Copyright 2012, Red Hat, Inc., and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.gatein.api.management; import org.exoplatform.container.PortalContainer; import org.exoplatform.portal.mop.SiteKey; import org.exoplatform.web.WebAppController; import org.exoplatform.web.url.navigation.NavigationResource; import org.exoplatform.web.url.simple.SimpleURL; import org.exoplatform.web.url.simple.SimpleURLContext; import org.gatein.api.BasicPortalRequest; import org.gatein.api.EntityAlreadyExistsException; import org.gatein.api.EntityNotFoundException; import org.gatein.api.Portal; import org.gatein.api.PortalRequest; import org.gatein.api.Util; import org.gatein.api.common.Attributes; import org.gatein.api.common.Pagination; import org.gatein.api.common.URIResolver; import org.gatein.api.navigation.Navigation; import org.gatein.api.navigation.NodePath; import org.gatein.api.security.Group; import org.gatein.api.security.Permission; import org.gatein.api.security.User; import org.gatein.api.site.Site; import org.gatein.api.site.SiteId; import org.gatein.api.site.SiteQuery; import org.gatein.api.site.SiteType; import org.gatein.common.logging.Logger; import org.gatein.common.logging.LoggerFactory; import org.gatein.management.api.ManagedUser; import org.gatein.management.api.PathAddress; import org.gatein.management.api.annotations.Managed; import org.gatein.management.api.annotations.ManagedAfter; import org.gatein.management.api.annotations.ManagedBefore; import org.gatein.management.api.annotations.ManagedContext; import org.gatein.management.api.annotations.ManagedOperation; import org.gatein.management.api.annotations.ManagedRole; import org.gatein.management.api.annotations.MappedAttribute; import org.gatein.management.api.annotations.MappedPath; import org.gatein.management.api.exceptions.ResourceNotFoundException; import org.gatein.management.api.model.ModelList; import org.gatein.management.api.model.ModelObject; import org.gatein.management.api.model.ModelProvider; import org.gatein.management.api.model.ModelReference; import org.gatein.management.api.model.ModelString; import org.gatein.management.api.model.ModelValue; import org.gatein.management.api.operation.OperationContext; import org.gatein.management.api.operation.OperationNames; import java.util.List; import java.util.Locale; import static org.gatein.api.management.Utils.*; /** * @author <a href="mailto:nscavell@redhat.com">Nick Scavelli</a> */ @SuppressWarnings("unused") @Managed(value = "api", description = "GateIn API Management Resource") public class GateInApiManagementResource { private static final Logger log = LoggerFactory.getLogger("org.gatein.api.management"); private static final SiteQuery SITE_QUERY = new SiteQuery.Builder().withSiteTypes(SiteType.SITE).build(); private static final SiteQuery SPACE_QUERY = new SiteQuery.Builder().withSiteTypes(SiteType.SPACE).build(); private static final SiteQuery DASHBOARD_QUERY = new SiteQuery.Builder().withSiteTypes(SiteType.DASHBOARD).build(); private final Portal portal; @ManagedContext private final ModelProvider modelProvider; // gatein-management will set this field via reflection public GateInApiManagementResource(Portal portal) { this(portal, null); } // Constructor for testing to specify ModelProvider instead of gatein-management GateInApiManagementResource(Portal portal, ModelProvider modelProvider) { this.portal = portal; this.modelProvider = modelProvider; } @ManagedBefore public void before(@ManagedContext OperationContext context) { PortalRequest portalRequest = PortalRequest.getInstance(); if (portalRequest == null) { setCurrentPortalRequest(context); } } @ManagedAfter public void after() { if (PortalRequest.getInstance() instanceof BasicPortalRequest) { BasicPortalRequest.setInstance(null); } } // ------------------------------------------------- Portal Sites --------------------------------------------------// @Managed("/sites") public ModelList getSites(@ManagedContext PathAddress address, @MappedAttribute("emptySites") String emptySites, @MappedAttribute("offset") String offset, @MappedAttribute("limit") String limit) { return _getSites(SITE_QUERY, address, emptySites, offset, limit); } @Managed("/sites/{site-name}") public ModelObject getSite(@MappedPath("site-name") String siteName, @ManagedContext OperationContext context) { SiteId id = new SiteId(siteName); return _getSite(id, context); } @Managed("/sites/{site-name}") @ManagedRole("administrators") @ManagedOperation(name = OperationNames.ADD_RESOURCE, description = "Adds a given site") public ModelObject addSite(@MappedPath("site-name") String siteName, @MappedAttribute("template") String template, @ManagedContext PathAddress address) { SiteId siteId = new SiteId(siteName); return _addSite(address, siteId, template); } @Managed("/sites/{site-name}") @ManagedRole("administrators") @ManagedOperation(name = OperationNames.REMOVE_RESOURCE, description = "Removes the given site") public void removeSite(@MappedPath("site-name") String siteName, @ManagedContext OperationContext context) { SiteId id = new SiteId(siteName); _removeSite(id, context); } @Managed("/sites/{site-name}") @ManagedRole("administrators") @ManagedOperation(name = OperationNames.UPDATE_RESOURCE, description = "Updates a given site") public ModelObject updateSite(@MappedPath("site-name") String siteName, @ManagedContext ModelObject siteModel, @ManagedContext OperationContext context) { SiteId id = new SiteId(siteName); return _updateSite(id, siteModel, context); } @Managed("/sites/{site-name}/pages") public PageManagementResource getPages(@MappedPath("site-name") String siteName, @ManagedContext OperationContext context) { SiteId id = new SiteId(siteName); return pagesResource(id, context); } @Managed("/sites/{site-name}/navigation") public NavigationManagementResource getNavigation(@MappedPath("site-name") String siteName, @ManagedContext OperationContext context) { SiteId id = new SiteId(siteName); return navigationResource(id, context); } // --------------------------------------------- Group Sites (Spaces) ----------------------------------------------// @Managed("/spaces") public ModelList getSpaces(@ManagedContext PathAddress address, @MappedAttribute("emptySites") String emptySites, @MappedAttribute("offset") String offset, @MappedAttribute("limit") String limit) { return _getSites(SPACE_QUERY, address, emptySites, offset, limit); } @Managed("/spaces/{group-name: .*}") public ModelObject getSpace(@MappedPath("group-name") String groupName, @ManagedContext OperationContext context) { SiteId id = new SiteId(new Group(groupName)); return _getSite(id, context); } @Managed("/spaces/{group-name: .*}") @ManagedRole("administrators") @ManagedOperation(name = OperationNames.ADD_RESOURCE, description = "Adds a given site") public ModelObject addSpace(@MappedPath("group-name") String groupName, @MappedAttribute("template") String template, @ManagedContext PathAddress address) { SiteId siteId = new SiteId(new Group(groupName)); return _addSite(address, siteId, template); } @Managed("/spaces/{group-name: .*}") @ManagedRole("administrators") @ManagedOperation(name = OperationNames.REMOVE_RESOURCE, description = "Removes the given space") public void removeSpace(@MappedPath("group-name") String groupName, @ManagedContext OperationContext context) { SiteId id = new SiteId(new Group(groupName)); _removeSite(id, context); } @Managed("/spaces/{group-name: .*}") @ManagedRole("administrators") @ManagedOperation(name = OperationNames.UPDATE_RESOURCE, description = "Updates a given space") public ModelObject updateSpace(@MappedPath("group-name") String groupName, @ManagedContext ModelObject siteModel, @ManagedContext OperationContext context) { SiteId id = new SiteId(new Group(groupName)); return _updateSite(id, siteModel, context); } @Managed("/spaces/{group-name: .*}/pages") public PageManagementResource getSpacePages(@MappedPath("group-name") String groupName, @ManagedContext OperationContext context) { SiteId id = new SiteId(new Group(groupName)); return pagesResource(id, context); } @Managed("/spaces/{group-name: .*}/navigation") public NavigationManagementResource getSpaceNavigation(@MappedPath("group-name") String groupName, @ManagedContext OperationContext context) { SiteId id = new SiteId(new Group(groupName)); return navigationResource(id, context); } // -------------------------------------------- User Sites (Dashboard) ---------------------------------------------// @Managed("/dashboards") public ModelList getDashboards(@ManagedContext PathAddress address, @MappedAttribute("emptySites") String emptySites, @MappedAttribute("offset") String offset, @MappedAttribute("limit") String limit) { return _getSites(DASHBOARD_QUERY, address, emptySites, offset, limit); } @Managed("/dashboards/{user-name}") public ModelObject getDashboard(@MappedPath("user-name") String userName, @ManagedContext OperationContext context) { SiteId id = new SiteId(new User(userName)); return _getSite(id, context); } @Managed("/dashboards/{user-name}") @ManagedRole("administrators") @ManagedOperation(name = OperationNames.ADD_RESOURCE, description = "Adds a given site") public ModelObject addDashboard(@MappedPath("user-name") String userName, @MappedAttribute("template") String template, @ManagedContext PathAddress address) { SiteId siteId = new SiteId(new User(userName)); return _addSite(address, siteId, template); } @Managed("/dashboards/{user-name}") @ManagedRole("administrators") @ManagedOperation(name = OperationNames.REMOVE_RESOURCE, description = "Removes the given dashboard") public void removeDashboard(@MappedPath("user-name") String userName, @ManagedContext OperationContext context) { SiteId id = new SiteId(new User(userName)); _removeSite(id, context); } @Managed("/dashboards/{user-name}") @ManagedRole("administrators") @ManagedOperation(name = OperationNames.UPDATE_RESOURCE, description = "Updates a given space") public ModelObject updateDashboard(@MappedPath("user-name") String userName, @ManagedContext ModelObject siteModel, @ManagedContext OperationContext context) { SiteId id = new SiteId(new User(userName)); return _updateSite(id, siteModel, context); } @Managed("/dashboards/{user-name}/pages") public PageManagementResource getDashboardPages(@MappedPath("user-name") String userName, @ManagedContext OperationContext context) { SiteId id = new SiteId(new User(userName)); return pagesResource(id, context); } @Managed("/dashboards/{user-name}/navigation") public NavigationManagementResource getDashboardNavigation(@MappedPath("user-name") String userName, @ManagedContext OperationContext context) { SiteId id = new SiteId(new User(userName)); return navigationResource(id, context); } private NavigationManagementResource navigationResource(SiteId siteId, OperationContext context) { requireSite(siteId, context); Navigation navigation = portal.getNavigation(siteId); if (navigation == null) { throw new ResourceNotFoundException("Navigation does not exist for site " + siteId); } return new NavigationManagementResource(navigation, modelProvider); } private PageManagementResource pagesResource(SiteId siteId, OperationContext context) { requireSite(siteId, context); return new PageManagementResource(portal, modelProvider, siteId); } private ModelList _getSites(SiteQuery query, PathAddress address, String emptySitesParam, String offsetParam, String limitParam) { boolean emptySites = Boolean.valueOf(emptySitesParam); Pagination pagination = getPagination(offsetParam, limitParam, query.getPagination()); query = new SiteQuery.Builder().from(query).includeEmptySites(emptySites).withPagination(pagination).build(); // Query sites List<Site> sites = portal.findSites(query); return populateModel(sites, modelProvider.newModel(ModelList.class), address); } private ModelObject _getSite(SiteId id, OperationContext context) { Site site = requireSite(id, context); // Populate site model return populateModel(site, modelProvider.newModel(ModelObject.class), context.getAddress()); } private ModelObject _addSite(PathAddress address, SiteId siteId, String template) { Site site; try { if (template == null) { site = portal.createSite(siteId); } else { site = portal.createSite(siteId, template); } } catch (EntityAlreadyExistsException e) { throw alreadyExists("Could not add site", siteId); } portal.saveSite(site); // Populate model return populateModel(site, modelProvider.newModel(ModelObject.class), address); } private void _removeSite(SiteId id, OperationContext context) { requireSite(id, context); try { boolean removed = portal.removeSite(id); if (!removed) throw new RuntimeException("Could not remove site + " + id + " for unknown reasons."); } catch (EntityNotFoundException e) { throw notFound("Cannot remove site", id); } } private ModelObject _updateSite(SiteId id, ModelObject siteModel, OperationContext context) { Site site = requireSite(id, context); if (siteModel.has("displayName")) { String displayName = get(siteModel, ModelString.class, "displayName").getValue(); site.setDisplayName(displayName); } if (siteModel.has("description")) { String description = get(siteModel, ModelString.class, "description").getValue(); site.setDescription(description); } if (siteModel.has("skin")) { String skin = get(siteModel, ModelString.class, "skin").getValue(); site.setSkin(skin); } if (siteModel.has("locale")) { Locale locale = getLocale(siteModel, "locale"); site.setLocale(locale); } if (siteModel.has("access-permissions")) { Permission permission = getPermission(siteModel, false, "access-permissions"); site.setAccessPermission(permission); } if (siteModel.has("edit-permissions")) { Permission permission = getPermission(siteModel, true, "edit-permissions"); site.setEditPermission(permission); } if (siteModel.hasDefined("attributes")) { ModelList list = get(siteModel, ModelList.class, "attributes"); for (int i = 0; i < list.size(); i++) { ModelValue mv = list.get(i); String field = "attributes["+i+"]"; // Used for error reporting if (mv.getValueType() != ModelValue.ModelValueType.OBJECT) { throw invalidType(mv, ModelValue.ModelValueType.OBJECT, field); } ModelObject attrModel = mv.asValue(ModelObject.class); if (!attrModel.hasDefined("key")) { throw requiredField(field, "key"); } String key = get(attrModel, ModelString.class, "key").getValue(); if (!attrModel.has("value")) { throw requiredField(field, "value"); } String value = get(attrModel, ModelString.class, "value").getValue(); site.getAttributes().put(key, value); } } portal.saveSite(site); return populateModel(site, modelProvider.newModel(ModelObject.class), context.getAddress()); } private Site requireSite(SiteId id, OperationContext context) { Site site = portal.getSite(id); if (site == null) throw new ResourceNotFoundException("Site not found for " + id); // Verify current user has access to site verifyAccess(site, context); return site; } private ModelObject populateModel(Site site, ModelObject siteModel, PathAddress address) { // Site fields siteModel.set("name", site.getId().getName()); siteModel.set("type", site.getId().getType().name().toLowerCase()); siteModel.set("displayName", site.getDisplayName()); siteModel.set("description", site.getDescription()); siteModel.set("skin", site.getSkin()); populate("locale", site.getLocale(), siteModel); populate("access-permissions", site.getAccessPermission(), siteModel); populate("edit-permissions", site.getEditPermission(), siteModel); ModelList attrList = siteModel.get("attributes", ModelList.class); Attributes attributes = site.getAttributes(); for (String key : attributes.keySet()) { ModelObject attr = attrList.add().setEmptyObject(); attr.set("key", key); attr.set("value", attributes.get(key)); } // Pages ModelReference pagesRef = siteModel.get("pages", ModelReference.class); pagesRef.set(address.append("pages")); // Navigation ModelReference navigationRef = siteModel.get("navigation", ModelReference.class); navigationRef.set(address.append("navigation")); return siteModel; } private ModelList populateModel(List<Site> sites, ModelList list, PathAddress address) { for (Site site : sites) { if (hasPermission(site.getAccessPermission())) { ModelReference siteRef = list.add().asValue(ModelReference.class); siteRef.set("name", site.getName()); siteRef.set("type", site.getType().getName()); siteRef.set(address.append(site.getName())); } } return list; } private User getUser(ManagedUser managedUser) { if (managedUser == null) return User.anonymous(); return new User(managedUser.getUserName()); } private void setCurrentPortalRequest(OperationContext context) { final ManagedUser managedUser = context.getUser(); final PathAddress address = context.getAddress(); // Retrieve siteId from address (can be null) SiteId siteId = getSiteId(address); // Retrieve nodePath from address (can be null) NodePath nodePath = getNodePath(address); Locale locale = context.getLocale(); // For some HTTP requests the locale is set to *, I guess to indicate a header 'Accept-Language: *' ? if (locale != null && locale.getLanguage().equals("*")) { locale = null; } User user = (managedUser == null || managedUser.getUserName() == null) ? User.anonymous() : new User(managedUser.getUserName()); final PortalContainer container = PortalContainer.getInstance(); final WebAppController controller = (WebAppController) container.getComponentInstanceOfType(WebAppController.class); URIResolver uriResolver = new URIResolver() { @Override public String resolveURI(SiteId siteId) { SiteKey siteKey = Util.from(siteId); NavigationResource navResource = new NavigationResource(siteKey, ""); SimpleURL url = new SimpleURL(new SimpleURLContext(container, controller)); url.setSchemeUse(false); url.setAuthorityUse(false); String urlString = url.setResource(navResource).toString(); return urlString.substring(0, urlString.length() - 1); } }; BasicPortalRequest.setInstance(new BasicPortalRequest(user, siteId, nodePath, locale, portal, uriResolver)); } private static SiteId getSiteId(PathAddress address) { String siteName = address.resolvePathTemplate("site-name"); if (siteName != null) { return new SiteId(siteName); } String groupName = address.resolvePathTemplate("group-name"); if (groupName != null) { return new SiteId(new Group(groupName)); } String userName = address.resolvePathTemplate("user-name"); if (userName != null) { return new SiteId(new User(userName)); } return null; } private static NodePath getNodePath(PathAddress address) { String path = address.resolvePathTemplate("path"); if (path != null) { return NodePath.fromString(path); } return null; } static PathAddress getSiteAddress(SiteId siteId) { PathAddress address = PathAddress.pathAddress("api"); switch (siteId.getType()) { case SITE: address = address.append("sites"); break; case SPACE: address = address.append("spaces"); break; case DASHBOARD: address = address.append("dashboards"); break; default: throw new AssertionError(); } return address.append(siteId.getName()); } static PathAddress getPagesAddress(SiteId siteId) { return getSiteAddress(siteId).append("pages"); } static PathAddress getNavigationAddress(SiteId siteId) { return getSiteAddress(siteId).append("navigation"); } }