/* * Copyright (c) 2010 Red Hat, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.ovirt.engine.api.restapi.util; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map.Entry; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import javax.ws.rs.Path; import org.ovirt.engine.api.model.ActionableResource; import org.ovirt.engine.api.model.AffinityGroup; import org.ovirt.engine.api.model.AffinityLabel; import org.ovirt.engine.api.model.Agent; import org.ovirt.engine.api.model.Application; import org.ovirt.engine.api.model.Balance; import org.ovirt.engine.api.model.BaseResource; import org.ovirt.engine.api.model.Bookmark; import org.ovirt.engine.api.model.Cdrom; import org.ovirt.engine.api.model.Cluster; import org.ovirt.engine.api.model.ClusterLevel; import org.ovirt.engine.api.model.CpuProfile; import org.ovirt.engine.api.model.DataCenter; import org.ovirt.engine.api.model.Disk; import org.ovirt.engine.api.model.DiskAttachment; import org.ovirt.engine.api.model.DiskProfile; import org.ovirt.engine.api.model.DiskSnapshot; import org.ovirt.engine.api.model.Domain; import org.ovirt.engine.api.model.Event; import org.ovirt.engine.api.model.ExternalComputeResource; import org.ovirt.engine.api.model.ExternalDiscoveredHost; import org.ovirt.engine.api.model.ExternalHost; import org.ovirt.engine.api.model.ExternalHostGroup; import org.ovirt.engine.api.model.ExternalHostProvider; import org.ovirt.engine.api.model.ExternalVmImport; import org.ovirt.engine.api.model.File; import org.ovirt.engine.api.model.Filter; import org.ovirt.engine.api.model.GlusterBrick; import org.ovirt.engine.api.model.GlusterHook; import org.ovirt.engine.api.model.GlusterVolume; import org.ovirt.engine.api.model.GlusterVolumeProfileDetails; import org.ovirt.engine.api.model.GraphicsConsole; import org.ovirt.engine.api.model.Group; import org.ovirt.engine.api.model.Hook; import org.ovirt.engine.api.model.Host; import org.ovirt.engine.api.model.HostDevice; import org.ovirt.engine.api.model.HostDevices; import org.ovirt.engine.api.model.HostNic; import org.ovirt.engine.api.model.HostStorage; import org.ovirt.engine.api.model.Icon; import org.ovirt.engine.api.model.Image; import org.ovirt.engine.api.model.ImageTransfer; import org.ovirt.engine.api.model.InstanceType; import org.ovirt.engine.api.model.IscsiBond; import org.ovirt.engine.api.model.Job; import org.ovirt.engine.api.model.KatelloErratum; import org.ovirt.engine.api.model.MacPool; import org.ovirt.engine.api.model.Network; import org.ovirt.engine.api.model.NetworkAttachment; import org.ovirt.engine.api.model.NetworkFilter; import org.ovirt.engine.api.model.NetworkLabel; import org.ovirt.engine.api.model.Nic; import org.ovirt.engine.api.model.NumaNode; import org.ovirt.engine.api.model.OpenStackImage; import org.ovirt.engine.api.model.OpenStackImageProvider; import org.ovirt.engine.api.model.OpenStackNetwork; import org.ovirt.engine.api.model.OpenStackNetworkProvider; import org.ovirt.engine.api.model.OpenStackSubnet; import org.ovirt.engine.api.model.OpenStackVolumeProvider; import org.ovirt.engine.api.model.OpenStackVolumeType; import org.ovirt.engine.api.model.OpenstackVolumeAuthenticationKey; import org.ovirt.engine.api.model.OperatingSystemInfo; import org.ovirt.engine.api.model.Permission; import org.ovirt.engine.api.model.Permit; import org.ovirt.engine.api.model.Qos; import org.ovirt.engine.api.model.Quota; import org.ovirt.engine.api.model.QuotaClusterLimit; import org.ovirt.engine.api.model.QuotaStorageLimit; import org.ovirt.engine.api.model.ReportedDevice; import org.ovirt.engine.api.model.Role; import org.ovirt.engine.api.model.SchedulingPolicy; import org.ovirt.engine.api.model.SchedulingPolicyUnit; import org.ovirt.engine.api.model.Session; import org.ovirt.engine.api.model.Snapshot; import org.ovirt.engine.api.model.SshPublicKey; import org.ovirt.engine.api.model.Statistic; import org.ovirt.engine.api.model.Step; import org.ovirt.engine.api.model.StorageConnection; import org.ovirt.engine.api.model.StorageConnectionExtension; import org.ovirt.engine.api.model.StorageDomain; import org.ovirt.engine.api.model.Tag; import org.ovirt.engine.api.model.Template; import org.ovirt.engine.api.model.UnmanagedNetwork; import org.ovirt.engine.api.model.User; import org.ovirt.engine.api.model.VirtualNumaNode; import org.ovirt.engine.api.model.Vm; import org.ovirt.engine.api.model.VmPool; import org.ovirt.engine.api.model.VnicProfile; import org.ovirt.engine.api.model.Watchdog; import org.ovirt.engine.api.model.Weight; import org.ovirt.engine.api.resource.AffinityGroupResource; import org.ovirt.engine.api.resource.AffinityGroupsResource; import org.ovirt.engine.api.resource.AffinityLabelHostResource; import org.ovirt.engine.api.resource.AffinityLabelHostsResource; import org.ovirt.engine.api.resource.AffinityLabelResource; import org.ovirt.engine.api.resource.AffinityLabelVmResource; import org.ovirt.engine.api.resource.AffinityLabelVmsResource; import org.ovirt.engine.api.resource.AffinityLabelsResource; import org.ovirt.engine.api.resource.AssignedAffinityLabelResource; import org.ovirt.engine.api.resource.AssignedAffinityLabelsResource; import org.ovirt.engine.api.resource.AssignedPermissionsResource; import org.ovirt.engine.api.resource.AssignedRolesResource; import org.ovirt.engine.api.resource.AssignedTagResource; import org.ovirt.engine.api.resource.AssignedTagsResource; import org.ovirt.engine.api.resource.AttachedStorageDomainResource; import org.ovirt.engine.api.resource.AttachedStorageDomainsResource; import org.ovirt.engine.api.resource.BalanceResource; import org.ovirt.engine.api.resource.BalancesResource; import org.ovirt.engine.api.resource.BookmarkResource; import org.ovirt.engine.api.resource.BookmarksResource; import org.ovirt.engine.api.resource.ClusterLevelResource; import org.ovirt.engine.api.resource.ClusterLevelsResource; import org.ovirt.engine.api.resource.ClusterNetworkResource; import org.ovirt.engine.api.resource.ClusterNetworksResource; import org.ovirt.engine.api.resource.ClusterResource; import org.ovirt.engine.api.resource.ClustersResource; import org.ovirt.engine.api.resource.CpuProfileResource; import org.ovirt.engine.api.resource.CpuProfilesResource; import org.ovirt.engine.api.resource.DataCenterResource; import org.ovirt.engine.api.resource.DataCentersResource; import org.ovirt.engine.api.resource.DiskAttachmentResource; import org.ovirt.engine.api.resource.DiskAttachmentsResource; import org.ovirt.engine.api.resource.DiskProfileResource; import org.ovirt.engine.api.resource.DiskProfilesResource; import org.ovirt.engine.api.resource.DiskResource; import org.ovirt.engine.api.resource.DiskSnapshotResource; import org.ovirt.engine.api.resource.DiskSnapshotsResource; import org.ovirt.engine.api.resource.DisksResource; import org.ovirt.engine.api.resource.EventResource; import org.ovirt.engine.api.resource.EventsResource; import org.ovirt.engine.api.resource.ExternalVmImportsResource; import org.ovirt.engine.api.resource.FenceAgentResource; import org.ovirt.engine.api.resource.FenceAgentsResource; import org.ovirt.engine.api.resource.FileResource; import org.ovirt.engine.api.resource.FilesResource; import org.ovirt.engine.api.resource.FilterResource; import org.ovirt.engine.api.resource.FiltersResource; import org.ovirt.engine.api.resource.HostDeviceResource; import org.ovirt.engine.api.resource.HostDevicesResource; import org.ovirt.engine.api.resource.HostHookResource; import org.ovirt.engine.api.resource.HostHooksResource; import org.ovirt.engine.api.resource.HostNicResource; import org.ovirt.engine.api.resource.HostNicsResource; import org.ovirt.engine.api.resource.HostNumaNodeResource; import org.ovirt.engine.api.resource.HostNumaNodesResource; import org.ovirt.engine.api.resource.HostStorageResource; import org.ovirt.engine.api.resource.IconResource; import org.ovirt.engine.api.resource.IconsResource; import org.ovirt.engine.api.resource.ImageResource; import org.ovirt.engine.api.resource.ImageTransferResource; import org.ovirt.engine.api.resource.ImageTransfersResource; import org.ovirt.engine.api.resource.ImagesResource; import org.ovirt.engine.api.resource.InstanceTypeGraphicsConsoleResource; import org.ovirt.engine.api.resource.InstanceTypeGraphicsConsolesResource; import org.ovirt.engine.api.resource.InstanceTypeNicResource; import org.ovirt.engine.api.resource.InstanceTypeNicsResource; import org.ovirt.engine.api.resource.InstanceTypeResource; import org.ovirt.engine.api.resource.InstanceTypeWatchdogResource; import org.ovirt.engine.api.resource.InstanceTypeWatchdogsResource; import org.ovirt.engine.api.resource.InstanceTypesResource; import org.ovirt.engine.api.resource.IscsiBondResource; import org.ovirt.engine.api.resource.IscsiBondsResource; import org.ovirt.engine.api.resource.JobResource; import org.ovirt.engine.api.resource.JobsResource; import org.ovirt.engine.api.resource.MacPoolResource; import org.ovirt.engine.api.resource.MacPoolsResource; import org.ovirt.engine.api.resource.NetworkAttachmentResource; import org.ovirt.engine.api.resource.NetworkAttachmentsResource; import org.ovirt.engine.api.resource.NetworkFilterResource; import org.ovirt.engine.api.resource.NetworkFiltersResource; import org.ovirt.engine.api.resource.NetworkLabelResource; import org.ovirt.engine.api.resource.NetworkLabelsResource; import org.ovirt.engine.api.resource.NetworkResource; import org.ovirt.engine.api.resource.NetworksResource; import org.ovirt.engine.api.resource.OperatingSystemResource; import org.ovirt.engine.api.resource.OperatingSystemsResource; import org.ovirt.engine.api.resource.PermissionResource; import org.ovirt.engine.api.resource.PermitResource; import org.ovirt.engine.api.resource.PermitsResource; import org.ovirt.engine.api.resource.QosResource; import org.ovirt.engine.api.resource.QossResource; import org.ovirt.engine.api.resource.QuotaClusterLimitResource; import org.ovirt.engine.api.resource.QuotaClusterLimitsResource; import org.ovirt.engine.api.resource.QuotaResource; import org.ovirt.engine.api.resource.QuotaStorageLimitResource; import org.ovirt.engine.api.resource.QuotaStorageLimitsResource; import org.ovirt.engine.api.resource.QuotasResource; import org.ovirt.engine.api.resource.RoleResource; import org.ovirt.engine.api.resource.RolesResource; import org.ovirt.engine.api.resource.SchedulingPoliciesResource; import org.ovirt.engine.api.resource.SchedulingPolicyResource; import org.ovirt.engine.api.resource.SchedulingPolicyUnitResource; import org.ovirt.engine.api.resource.SchedulingPolicyUnitsResource; import org.ovirt.engine.api.resource.SnapshotResource; import org.ovirt.engine.api.resource.SnapshotsResource; import org.ovirt.engine.api.resource.StatisticResource; import org.ovirt.engine.api.resource.StatisticsResource; import org.ovirt.engine.api.resource.StepResource; import org.ovirt.engine.api.resource.StepsResource; import org.ovirt.engine.api.resource.StorageDomainResource; import org.ovirt.engine.api.resource.StorageDomainTemplateResource; import org.ovirt.engine.api.resource.StorageDomainTemplatesResource; import org.ovirt.engine.api.resource.StorageDomainVmResource; import org.ovirt.engine.api.resource.StorageDomainVmsResource; import org.ovirt.engine.api.resource.StorageDomainsResource; import org.ovirt.engine.api.resource.StorageResource; import org.ovirt.engine.api.resource.StorageServerConnectionExtensionResource; import org.ovirt.engine.api.resource.StorageServerConnectionExtensionsResource; import org.ovirt.engine.api.resource.StorageServerConnectionResource; import org.ovirt.engine.api.resource.StorageServerConnectionsResource; import org.ovirt.engine.api.resource.SystemPermissionsResource; import org.ovirt.engine.api.resource.SystemResource; import org.ovirt.engine.api.resource.TagResource; import org.ovirt.engine.api.resource.TagsResource; import org.ovirt.engine.api.resource.TemplateCdromResource; import org.ovirt.engine.api.resource.TemplateCdromsResource; import org.ovirt.engine.api.resource.TemplateDiskResource; import org.ovirt.engine.api.resource.TemplateDisksResource; import org.ovirt.engine.api.resource.TemplateGraphicsConsoleResource; import org.ovirt.engine.api.resource.TemplateGraphicsConsolesResource; import org.ovirt.engine.api.resource.TemplateNicResource; import org.ovirt.engine.api.resource.TemplateNicsResource; import org.ovirt.engine.api.resource.TemplateResource; import org.ovirt.engine.api.resource.TemplateWatchdogResource; import org.ovirt.engine.api.resource.TemplateWatchdogsResource; import org.ovirt.engine.api.resource.TemplatesResource; import org.ovirt.engine.api.resource.UnmanagedNetworkResource; import org.ovirt.engine.api.resource.UnmanagedNetworksResource; import org.ovirt.engine.api.resource.VirtualFunctionAllowedNetworkResource; import org.ovirt.engine.api.resource.VirtualFunctionAllowedNetworksResource; import org.ovirt.engine.api.resource.VmApplicationResource; import org.ovirt.engine.api.resource.VmApplicationsResource; import org.ovirt.engine.api.resource.VmCdromResource; import org.ovirt.engine.api.resource.VmCdromsResource; import org.ovirt.engine.api.resource.VmDiskResource; import org.ovirt.engine.api.resource.VmDisksResource; import org.ovirt.engine.api.resource.VmGraphicsConsoleResource; import org.ovirt.engine.api.resource.VmGraphicsConsolesResource; import org.ovirt.engine.api.resource.VmHostDeviceResource; import org.ovirt.engine.api.resource.VmHostDevicesResource; import org.ovirt.engine.api.resource.VmNicResource; import org.ovirt.engine.api.resource.VmNicsResource; import org.ovirt.engine.api.resource.VmNumaNodeResource; import org.ovirt.engine.api.resource.VmNumaNodesResource; import org.ovirt.engine.api.resource.VmPoolResource; import org.ovirt.engine.api.resource.VmPoolsResource; import org.ovirt.engine.api.resource.VmReportedDeviceResource; import org.ovirt.engine.api.resource.VmReportedDevicesResource; import org.ovirt.engine.api.resource.VmResource; import org.ovirt.engine.api.resource.VmSessionResource; import org.ovirt.engine.api.resource.VmSessionsResource; import org.ovirt.engine.api.resource.VmWatchdogResource; import org.ovirt.engine.api.resource.VmWatchdogsResource; import org.ovirt.engine.api.resource.VmsResource; import org.ovirt.engine.api.resource.VnicProfileResource; import org.ovirt.engine.api.resource.VnicProfilesResource; import org.ovirt.engine.api.resource.WeightResource; import org.ovirt.engine.api.resource.WeightsResource; import org.ovirt.engine.api.resource.aaa.DomainGroupResource; import org.ovirt.engine.api.resource.aaa.DomainGroupsResource; import org.ovirt.engine.api.resource.aaa.DomainResource; import org.ovirt.engine.api.resource.aaa.DomainUserResource; import org.ovirt.engine.api.resource.aaa.DomainUsersResource; import org.ovirt.engine.api.resource.aaa.DomainsResource; import org.ovirt.engine.api.resource.aaa.GroupResource; import org.ovirt.engine.api.resource.aaa.GroupsResource; import org.ovirt.engine.api.resource.aaa.SshPublicKeyResource; import org.ovirt.engine.api.resource.aaa.SshPublicKeysResource; import org.ovirt.engine.api.resource.aaa.UserResource; import org.ovirt.engine.api.resource.aaa.UsersResource; import org.ovirt.engine.api.resource.externalhostproviders.EngineKatelloErrataResource; import org.ovirt.engine.api.resource.externalhostproviders.ExternalComputeResourceResource; import org.ovirt.engine.api.resource.externalhostproviders.ExternalComputeResourcesResource; import org.ovirt.engine.api.resource.externalhostproviders.ExternalDiscoveredHostResource; import org.ovirt.engine.api.resource.externalhostproviders.ExternalDiscoveredHostsResource; import org.ovirt.engine.api.resource.externalhostproviders.ExternalHostGroupResource; import org.ovirt.engine.api.resource.externalhostproviders.ExternalHostGroupsResource; import org.ovirt.engine.api.resource.externalhostproviders.ExternalHostProviderResource; import org.ovirt.engine.api.resource.externalhostproviders.ExternalHostProvidersResource; import org.ovirt.engine.api.resource.externalhostproviders.ExternalHostResource; import org.ovirt.engine.api.resource.externalhostproviders.ExternalHostsResource; import org.ovirt.engine.api.resource.externalhostproviders.KatelloErrataResource; import org.ovirt.engine.api.resource.externalhostproviders.KatelloErratumResource; import org.ovirt.engine.api.resource.gluster.GlusterBrickResource; import org.ovirt.engine.api.resource.gluster.GlusterBricksResource; import org.ovirt.engine.api.resource.gluster.GlusterHookResource; import org.ovirt.engine.api.resource.gluster.GlusterHooksResource; import org.ovirt.engine.api.resource.gluster.GlusterVolumeResource; import org.ovirt.engine.api.resource.gluster.GlusterVolumesResource; import org.ovirt.engine.api.resource.openstack.OpenstackImageProviderResource; import org.ovirt.engine.api.resource.openstack.OpenstackImageProvidersResource; import org.ovirt.engine.api.resource.openstack.OpenstackImageResource; import org.ovirt.engine.api.resource.openstack.OpenstackImagesResource; import org.ovirt.engine.api.resource.openstack.OpenstackNetworkProviderResource; import org.ovirt.engine.api.resource.openstack.OpenstackNetworkProvidersResource; import org.ovirt.engine.api.resource.openstack.OpenstackNetworkResource; import org.ovirt.engine.api.resource.openstack.OpenstackNetworksResource; import org.ovirt.engine.api.resource.openstack.OpenstackSubnetResource; import org.ovirt.engine.api.resource.openstack.OpenstackSubnetsResource; import org.ovirt.engine.api.resource.openstack.OpenstackVolumeAuthenticationKeyResource; import org.ovirt.engine.api.resource.openstack.OpenstackVolumeAuthenticationKeysResource; import org.ovirt.engine.api.resource.openstack.OpenstackVolumeProviderResource; import org.ovirt.engine.api.resource.openstack.OpenstackVolumeProvidersResource; import org.ovirt.engine.api.resource.openstack.OpenstackVolumeTypeResource; import org.ovirt.engine.api.resource.openstack.OpenstackVolumeTypesResource; import org.ovirt.engine.api.restapi.invocation.Current; import org.ovirt.engine.api.restapi.invocation.CurrentManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Contains a static addLinks() method which constructs any href attributes * and action links required by a representation. * * The information used to build links is obtained from the annotations on * the API definition interfaces. * For example, a link to a VM is the combination of the @Path attribute on * VmsResource and the VM id - i.e. '/restapi-definition/vms/{vm_id}' * * Resource collections which are a sub-resource of a parent collection * present a more difficult challenge. For example, the link to a VM tag * is the combination of the @Path attribute on VmsResource, the VM id, * the @Path attribute on VmResource.getTagsResource() and the tag id - * i.e. '/restapi-definition/vms/{vm_id}/tags/{tag_id}' * In most cases the parent type may be computed, but in exceptional * cases there are a number of equally valid candidates. Disambiguation * is achieved via an explicit suggestedParentType parameter. * * To be able to do this we need, for each collection, the collection type * (e.g. AssignedTagsResource), the resource type (e.g. AssignedTagResource) * and the parent model type (e.g. VM). The TYPES map below is populated * with this information for every resource type. */ public class LinkHelper { private static final Logger log = LoggerFactory.getLogger(LinkHelper.class); private static final String SEARCH_RELATION = "/search"; private static final String SEARCH_TEMPLATE = "?search={query}"; private static final String MATRIX_PARAMETER_TEMPLATE = ";%s={%s}"; /** * A constant representing the pseudo-parent of a top-level collection */ private static final Class<? extends BaseResource> NO_PARENT = BaseResource.class; /** * A map describing every possible collection */ private static EntityLocationMap TYPES = new EntityLocationMap(); /** * A map for caching relevant resource methods for each class */ private static ConcurrentMap<Class<?>, List<Method>> methodCache = new ConcurrentHashMap<>(); /** * This class serves as a key to a map which stores values of 'Path' annotations * found in Service interfaces. A 'collection' service (e.g: VmsService) along * with a single-entity Service (e.g: VmService) identify a location on the API * tree, which may be associated with a value of a 'Path' annotation. */ private static class PathKey { private Class<?> service; private Class<?> parentService; public PathKey(Class<?> service, Class<?> parentService) { super(); this.service = service; this.parentService = parentService; } @Override public boolean equals(Object obj) { if (obj instanceof PathKey) { PathKey key = (PathKey)obj; return equals(service, key.service) && equals(parentService, key.parentService); } else { return false; } } private boolean equals(Class<?> class1, Class<?> class2) { return Objects.equals(class1, class2); } @Override public int hashCode() { if (service==null && parentService==null) { return 0; } if (service==null) { return parentService.hashCode(); } if (parentService==null) { return service.hashCode(); } return 997 * (service.hashCode()) ^ 991 * (parentService.hashCode()); //large primes! } } private static ConcurrentMap<PathKey, String> pathCache = new ConcurrentHashMap<>(); static { LocationByParentMap map; map = new LocationByParentMap(TemplateCdromResource.class, TemplateCdromsResource.class, Template.class); map.add(VmCdromResource.class, VmCdromsResource.class, Vm.class); TYPES.put(Cdrom.class, map); map = new LocationByParentMap(VmGraphicsConsoleResource.class, VmGraphicsConsolesResource.class, Vm.class); map.add(InstanceTypeGraphicsConsoleResource.class, InstanceTypeGraphicsConsolesResource.class, InstanceType.class); map.add(TemplateGraphicsConsoleResource.class, TemplateGraphicsConsolesResource.class, Template.class); TYPES.put(GraphicsConsole.class, map); map = new LocationByParentMap(VmApplicationResource.class, VmApplicationsResource.class, Vm.class); TYPES.put(Application.class, map); map = new LocationByParentMap(VmReportedDeviceResource.class, VmReportedDevicesResource.class, Vm.class); TYPES.put(ReportedDevice.class, map); map = new LocationByParentMap(ClusterResource.class, ClustersResource.class); TYPES.put(Cluster.class, map); map = new LocationByParentMap(DataCenterResource.class, DataCentersResource.class); TYPES.put(DataCenter.class, map); map = new LocationByParentMap(MacPoolResource.class, MacPoolsResource.class); TYPES.put(MacPool.class, map); map = new LocationByParentMap(NetworkFilterResource.class, NetworkFiltersResource.class); TYPES.put(NetworkFilter.class, map); map = new LocationByParentMap(DiskResource.class, DisksResource.class); map.add(VmDiskResource.class, VmDisksResource.class, Vm.class); map.add(TemplateDiskResource.class, TemplateDisksResource.class, Template.class); TYPES.put(Disk.class, map); map = new LocationByParentMap(DiskSnapshotResource.class, DiskSnapshotsResource.class, StorageDomain.class); TYPES.put(DiskSnapshot.class, map); map = new LocationByParentMap(StorageServerConnectionExtensionResource.class, StorageServerConnectionExtensionsResource.class, Host.class); TYPES.put(StorageConnectionExtension.class, map); map = new LocationByParentMap(); map.add(ImageTransferResource.class, ImageTransfersResource.class, NO_PARENT); TYPES.put(ImageTransfer.class, map); map = new LocationByParentMap(org.ovirt.engine.api.resource.HostResource.class, org.ovirt.engine.api.resource.HostsResource.class); map.add(AffinityLabelHostResource.class, AffinityLabelHostsResource.class, AffinityLabel.class); TYPES.put(Host.class, map); map = new LocationByParentMap(HostNicResource.class, HostNicsResource.class, Host.class); TYPES.put(HostNic.class, map); map = new LocationByParentMap(HostNumaNodeResource.class, HostNumaNodesResource.class, Host.class); TYPES.put(NumaNode.class, map); map = new LocationByParentMap(HostHookResource.class, HostHooksResource.class, Host.class); TYPES.put(Hook.class, map); map = new LocationByParentMap(FileResource.class, FilesResource.class, StorageDomain.class); TYPES.put(File.class, map); map = new LocationByParentMap(ImageResource.class, ImagesResource.class, StorageDomain.class); map.add(ImageTransferResource.class, ImageTransfersResource.class, ImageTransfer.class); TYPES.put(Image.class, map); map = new LocationByParentMap(GroupResource.class, GroupsResource.class); map.add(DomainGroupResource.class, DomainGroupsResource.class, Domain.class); TYPES.put(Group.class, map); map = new LocationByParentMap(PermissionResource.class, AssignedPermissionsResource.class, User.class); map.add(PermissionResource.class, AssignedPermissionsResource.class, Group.class); map.add(PermissionResource.class, AssignedPermissionsResource.class, Role.class); map.add(PermissionResource.class, AssignedPermissionsResource.class, Vm.class); map.add(PermissionResource.class, AssignedPermissionsResource.class, Disk.class); map.add(PermissionResource.class, SystemPermissionsResource.class, NO_PARENT); TYPES.put(Permission.class, map); map = new LocationByParentMap(NetworkResource.class, NetworksResource.class); map.add(ClusterNetworkResource.class, ClusterNetworksResource.class, Cluster.class); map.add(NetworkResource.class, NetworksResource.class, Network.class); map.add(VirtualFunctionAllowedNetworkResource.class, VirtualFunctionAllowedNetworksResource.class, HostNic.class); TYPES.put(Network.class, map); map = new LocationByParentMap(); map.add(InstanceTypeNicResource.class, InstanceTypeNicsResource.class, InstanceType.class); map.add(TemplateNicResource.class, TemplateNicsResource.class, Template.class); map.add(VmNicResource.class, VmNicsResource.class, Vm.class); TYPES.put(Nic.class, map); map = new LocationByParentMap(VmNumaNodeResource.class, VmNumaNodesResource.class, Vm.class); TYPES.put(VirtualNumaNode.class, map); map = new LocationByParentMap(PermitResource.class, PermitsResource.class, Role.class); TYPES.put(Permit.class, map); map = new LocationByParentMap(RoleResource.class, RolesResource.class); map.add(RoleResource.class, AssignedRolesResource.class, User.class); TYPES.put(Role.class, map); map = new LocationByParentMap(SnapshotResource.class, SnapshotsResource.class, Vm.class); TYPES.put(Snapshot.class, map); map = new LocationByParentMap(StorageResource.class, HostStorageResource.class, Host.class); TYPES.put(HostStorage.class, map); map = new LocationByParentMap(StorageServerConnectionResource.class, StorageServerConnectionsResource.class); TYPES.put(StorageConnection.class, map); map = new LocationByParentMap(StorageDomainResource.class, StorageDomainsResource.class); map.add(AttachedStorageDomainResource.class, AttachedStorageDomainsResource.class, DataCenter.class); TYPES.put(StorageDomain.class, map); map = new LocationByParentMap(TagResource.class, TagsResource.class); map.add(AssignedTagResource.class, AssignedTagsResource.class, Host.class); map.add(AssignedTagResource.class, AssignedTagsResource.class, User.class); map.add(AssignedTagResource.class, AssignedTagsResource.class, Vm.class); map.add(AssignedTagResource.class, AssignedTagsResource.class, Template.class); map.add(AssignedTagResource.class, AssignedTagsResource.class, Group.class); TYPES.put(Tag.class, map); map = new LocationByParentMap(BookmarkResource.class, BookmarksResource.class); TYPES.put(Bookmark.class, map); map = new LocationByParentMap(IconResource.class, IconsResource.class); TYPES.put(Icon.class, map); map = new LocationByParentMap(TemplateResource.class, TemplatesResource.class); map.add(StorageDomainTemplateResource.class, StorageDomainTemplatesResource.class, StorageDomain.class); TYPES.put(Template.class, map); map = new LocationByParentMap(InstanceTypeResource.class, InstanceTypesResource.class); TYPES.put(InstanceType.class, map); map = new LocationByParentMap(UserResource.class, UsersResource.class); map.add(DomainUserResource.class, DomainUsersResource.class, Domain.class); TYPES.put(User.class, map); map = new LocationByParentMap(VmResource.class, VmsResource.class); map.add(StorageDomainVmResource.class, StorageDomainVmsResource.class, StorageDomain.class); map.add(AffinityLabelVmResource.class, AffinityLabelVmsResource.class, AffinityLabel.class); // map.add(SnapshotResource.class, SnapshotsResource.class, Snapshot.class); TYPES.put(Vm.class, map); map = new LocationByParentMap(VmPoolResource.class, VmPoolsResource.class); TYPES.put(VmPool.class, map); map = new LocationByParentMap(EventResource.class, EventsResource.class); TYPES.put(Event.class, map); map = new LocationByParentMap(DomainResource.class, DomainsResource.class); TYPES.put(Domain.class, map); map = new LocationByParentMap(StatisticResource.class, StatisticsResource.class, Disk.class); map.add(StatisticResource.class, StatisticsResource.class, Host.class); map.add(StatisticResource.class, StatisticsResource.class, HostNic.class); map.add(StatisticResource.class, StatisticsResource.class, NumaNode.class); map.add(StatisticResource.class, StatisticsResource.class, Nic.class); map.add(StatisticResource.class, StatisticsResource.class, Vm.class); map.add(StatisticResource.class, StatisticsResource.class, GlusterBrick.class); TYPES.put(Statistic.class, map); map = new LocationByParentMap(QuotaResource.class, QuotasResource.class, DataCenter.class); TYPES.put(Quota.class, map); map = new LocationByParentMap(QuotaStorageLimitResource.class, QuotaStorageLimitsResource.class, Quota.class); TYPES.put(QuotaStorageLimit.class, map); map = new LocationByParentMap(QuotaClusterLimitResource.class, QuotaClusterLimitsResource.class, Quota.class); TYPES.put(QuotaClusterLimit.class, map); map = new LocationByParentMap(GlusterVolumeResource.class, GlusterVolumesResource.class, Cluster.class); TYPES.put(GlusterVolume.class, map); TYPES.put(GlusterVolumeProfileDetails.class, map); map = new LocationByParentMap(GlusterBrickResource.class, GlusterBricksResource.class, GlusterVolume.class); TYPES.put(GlusterBrick.class, map); map = new LocationByParentMap(GlusterHookResource.class, GlusterHooksResource.class, Cluster.class); TYPES.put(GlusterHook.class, map); map = new LocationByParentMap(); map.add(InstanceTypeWatchdogResource.class, InstanceTypeWatchdogsResource.class, InstanceType.class); map.add(TemplateWatchdogResource.class, TemplateWatchdogsResource.class, Template.class); map.add(VmWatchdogResource.class, VmWatchdogsResource.class, Vm.class); TYPES.put(Watchdog.class, map); map = new LocationByParentMap(JobResource.class, JobsResource.class); TYPES.put(Job.class, map); map = new LocationByParentMap(StepResource.class, StepsResource.class, Job.class); TYPES.put(Step.class, map); map = new LocationByParentMap(VnicProfileResource.class, VnicProfilesResource.class); TYPES.put(VnicProfile.class, map); map = new LocationByParentMap(NetworkLabelResource.class, NetworkLabelsResource.class); map.add(NetworkLabelResource.class, NetworkLabelsResource.class, Network.class); map.add(NetworkLabelResource.class, NetworkLabelsResource.class, HostNic.class); TYPES.put(NetworkLabel.class, map); map = new LocationByParentMap(NetworkAttachmentResource.class, NetworkAttachmentsResource.class, Host.class); map.add(NetworkAttachmentResource.class, NetworkAttachmentsResource.class, HostNic.class); TYPES.put(NetworkAttachment.class, map); map = new LocationByParentMap(AffinityLabelResource.class, AffinityLabelsResource.class); map.add(AssignedAffinityLabelResource.class, AssignedAffinityLabelsResource.class, Vm.class); map.add(AssignedAffinityLabelResource.class, AssignedAffinityLabelsResource.class, Host.class); map.add(AffinityLabelResource.class, AffinityLabelsResource.class, NO_PARENT); TYPES.put(AffinityLabel.class, map); map = new LocationByParentMap(UnmanagedNetworkResource.class, UnmanagedNetworksResource.class, Host.class); TYPES.put(UnmanagedNetwork.class, map); map = new LocationByParentMap(AffinityGroupResource.class, AffinityGroupsResource.class, Cluster.class); TYPES.put(AffinityGroup.class, map); map = new LocationByParentMap(VmSessionResource.class, VmSessionsResource.class, Vm.class); TYPES.put(Session.class, map); map = new LocationByParentMap(HostDevice.class, HostDevices.class); map.add(HostDeviceResource.class, HostDevicesResource.class, Host.class); map.add(VmHostDeviceResource.class, VmHostDevicesResource.class, Vm.class); TYPES.put(HostDevice.class, map); map = new LocationByParentMap(SchedulingPolicyUnitResource.class, SchedulingPolicyUnitsResource.class); TYPES.put(SchedulingPolicyUnit.class, map); map = new LocationByParentMap(SchedulingPolicyResource.class, SchedulingPoliciesResource.class); TYPES.put(SchedulingPolicy.class, map); map = new LocationByParentMap(FilterResource.class, FiltersResource.class, SchedulingPolicy.class); TYPES.put(Filter.class, map); map = new LocationByParentMap(WeightResource.class, WeightsResource.class, SchedulingPolicy.class); TYPES.put(Weight.class, map); map = new LocationByParentMap(BalanceResource.class, BalancesResource.class, SchedulingPolicy.class); TYPES.put(Balance.class, map); map = new LocationByParentMap(QosResource.class, QossResource.class, DataCenter.class); map.add(QosResource.class, QossResource.class, Network.class); TYPES.put(Qos.class, map); map = new LocationByParentMap(IscsiBondResource.class, IscsiBondsResource.class, DataCenter.class); TYPES.put(IscsiBond.class, map); map = new LocationByParentMap(DiskProfileResource.class, DiskProfilesResource.class); TYPES.put(DiskProfile.class, map); map = new LocationByParentMap(CpuProfileResource.class, CpuProfilesResource.class); TYPES.put(CpuProfile.class, map); // Operating systems: map = new LocationByParentMap(OperatingSystemResource.class, OperatingSystemsResource.class); TYPES.put(OperatingSystemInfo.class, map); // External host providers: map = new LocationByParentMap(ExternalHostProviderResource.class, ExternalHostProvidersResource.class); TYPES.put(ExternalHostProvider.class, map); map = new LocationByParentMap(ExternalHostResource.class, ExternalHostsResource.class); map.add(ExternalHostResource.class, ExternalHostsResource.class, ExternalHostProvider.class); TYPES.put(ExternalHost.class, map); map = new LocationByParentMap(ExternalDiscoveredHostResource.class, ExternalHostsResource.class); map.add(ExternalDiscoveredHostResource.class, ExternalDiscoveredHostsResource.class, ExternalHostProvider.class); TYPES.put(ExternalDiscoveredHost.class, map); map = new LocationByParentMap(ExternalHostGroupResource.class, ExternalHostGroupsResource.class); map.add(ExternalHostGroupResource.class, ExternalHostGroupsResource.class, ExternalHostProvider.class); TYPES.put(ExternalHostGroup.class, map); map = new LocationByParentMap(ExternalComputeResourceResource.class, ExternalComputeResourcesResource.class); map.add(ExternalComputeResourceResource.class, ExternalComputeResourcesResource.class, ExternalHostProvider.class); TYPES.put(ExternalComputeResource.class, map); // OpenStack image providers: map = new LocationByParentMap(OpenstackImageProviderResource.class, OpenstackImageProvidersResource.class); TYPES.put(OpenStackImageProvider.class, map); map = new LocationByParentMap(OpenstackImageResource.class, OpenstackImagesResource.class); map.add(OpenstackImageResource.class, OpenstackImagesResource.class, OpenStackImageProvider.class); TYPES.put(OpenStackImage.class, map); // OpenStack volume providers: map = new LocationByParentMap(OpenstackVolumeProviderResource.class, OpenstackVolumeProvidersResource.class); TYPES.put(OpenStackVolumeProvider.class, map); map = new LocationByParentMap(OpenstackVolumeTypeResource.class, OpenstackVolumeTypesResource.class); map.add(OpenstackVolumeTypeResource.class, OpenstackVolumeTypesResource.class, OpenStackVolumeProvider.class); TYPES.put(OpenStackVolumeType.class, map); map = new LocationByParentMap(OpenstackVolumeAuthenticationKeyResource.class, OpenstackVolumeAuthenticationKeysResource.class); map.add(OpenstackVolumeAuthenticationKeyResource.class, OpenstackVolumeAuthenticationKeysResource.class, OpenStackVolumeProvider.class); TYPES.put(OpenstackVolumeAuthenticationKey.class, map); // OpenStack network providers: map = new LocationByParentMap(OpenstackNetworkProviderResource.class, OpenstackNetworkProvidersResource.class); TYPES.put(OpenStackNetworkProvider.class, map); map = new LocationByParentMap(OpenstackNetworkResource.class, OpenstackNetworksResource.class); map.add(OpenstackNetworkResource.class, OpenstackNetworksResource.class, OpenStackNetworkProvider.class); TYPES.put(OpenStackNetwork.class, map); map = new LocationByParentMap(OpenstackSubnetResource.class, OpenstackSubnetsResource.class); map.add(OpenstackSubnetResource.class, OpenstackSubnetsResource.class, OpenStackNetwork.class); TYPES.put(OpenStackSubnet.class, map); map = new LocationByParentMap(FenceAgentResource.class, FenceAgentsResource.class, Host.class); TYPES.put(Agent.class, map); map = new LocationByParentMap(KatelloErratumResource.class, KatelloErrataResource.class, Host.class); map.add(KatelloErratumResource.class, KatelloErrataResource.class, Vm.class); map.add(KatelloErratumResource.class, EngineKatelloErrataResource.class, NO_PARENT); TYPES.put(KatelloErratum.class, map); map = new LocationByParentMap(); map.add(SshPublicKeyResource.class, SshPublicKeysResource.class, User.class); TYPES.put(SshPublicKey.class, map); map = new LocationByParentMap(); map.add(ClusterLevelResource.class, ClusterLevelsResource.class, NO_PARENT); TYPES.put(ClusterLevel.class, map); map = new LocationByParentMap(); map.add(DiskAttachmentResource.class, DiskAttachmentsResource.class, Vm.class); TYPES.put(DiskAttachment.class, map); map = new LocationByParentMap(ExternalVmImportsResource.class, ExternalVmImportsResource.class); TYPES.put(ExternalVmImport.class, map); } /** * Obtain the relative path to a top-level collection * * The path is the value of the {@link Path} annotation on resource locator method of the root resource that * returns a reference to this class of resource. For example, if the class is {@link BookmarksResource} then * returned value should be the value of the {@link Path} annotation on the * {@link SystemResource#getBookmarksResource()} method. * * @param service the collection resource type * @return the relative path to the collection */ private static String getRelativePath(Class<?> service) { return getRelativePath(service, SystemResource.class); } /** * Obtain the relative path to a sub-collection. * * The path is obtained from the @Path annotation on the method on @parent * which returns an instance of @clz. * * @param service the collection resource type (e.g. AssignedTagsResource) * @param parentService the parent resource type (e.g. VmResource) * @return the relative path to the collection */ private static String getRelativePath(Class<?> service, Class<?> parentService) { PathKey key = new PathKey(service, parentService); String path = pathCache.get(key); if (path!=null) { return path; } else { for (Method method : parentService.getMethods()) { if (method.getName().startsWith("get") && method.getReturnType() == service) { Path pathAnnotation = method.getAnnotation(Path.class); if (pathAnnotation != null) { pathCache.put(key, pathAnnotation.value()); return pathAnnotation.value(); } } } } log.warn("Can't find relative path for class \"" + service.getName() + "\", will return null"); return null; } /** * Obtain a set of inline BaseResource objects from @obj * * i.e. return the value of any properties on @obj which are a * sub-type of BaseResource * * @param obj the object to check * @return a list of any inline BaseResource objects */ private static List<BaseResource> getInlineResources(Object obj) { ArrayList<BaseResource> ret = new ArrayList<>(); for (Method method : getRelevantMethods(obj.getClass())) { // We need to recursively scan everything that is in the model package, as there may be references // to resources deeply nested: Object inline = null; try { inline = method.invoke(obj); } catch (Exception e) { // invocation target exception should not occur on simple getter } if (inline != null) { if (inline instanceof BaseResource) { ret.add((BaseResource) inline); } else { ret.addAll(getInlineResources(inline)); } } } return ret; } /** * Gets all the relevant possible inline resources methods of a class. Data is cached for future use. * @param clz * The class to examine * @return The list of relevant methods. */ private static List<Method> getRelevantMethods(Class<?> clz) { List<Method> methods = methodCache.get(clz); if (methods == null) { methods = new ArrayList<>(); for (Method method : clz.getMethods()) { if (method.getName().startsWith("get")) { if (method.getReturnType().getPackage() == BaseResource.class.getPackage()) { methods.add(method); } } } methodCache.put(clz, methods); } return methods; } /** * Unset the property on @model of type @type * * @param model the object with the property to unset * @param type the type of the property */ private static void unsetInlineResource(BaseResource model, Class<?> type) { for (Method method : model.getClass().getMethods()) { if (method.getName().startsWith("set")) { try { if (type.isAssignableFrom(method.getParameterTypes()[0])) { method.invoke(model, new Object[]{null}); return; } } catch (Exception e) { // invocation target exception should not occur on simple setter } } } } /** * Return any parent object set on @model * * i.e. return the value of any bean property whose type matches @parentType * * @param model object to check * @param parentType the type of the parent * @return the parent object, or null if not set */ private static <R extends BaseResource> BaseResource getParent(R model, Class<?> parentType) { for (Method method : getRelevantMethods(model.getClass())) { try { Object potentialParent = method.invoke(model); if (potentialParent != null && parentType.isAssignableFrom(potentialParent.getClass())) { return (BaseResource)potentialParent; } } catch (Exception e) { log.error("Error invoking method when adding links to an API entity", e); continue; } } return null; } /** * Lookup the #Collection instance which represents this object * * i.e. for a VM tag (i.e. a Tag object which its VM property set) * return the #Collection instance which encapsulates AssignedTagResource, * AssignedTagsResource and VM. * * @param model the object to query for * @return the #Collection instance representing the object's collection */ private static ApiLocationMetadata getCollection(BaseResource model) { return getLocationMetadata(model, null); } /** * Lookup the #Collection instance which represents this object * * i.e. for a VM tag (i.e. a Tag object which its VM property set) * return the #Collection instance which encapsulates AssignedTagResource, * AssignedTagsResource and VM. * * @param model the object to query for * @param suggestedParentType the suggested parent type * @return the #Collection instance representing the object's collection */ private static ApiLocationMetadata getLocationMetadata(BaseResource model, Class<? extends BaseResource> suggestedParentType) { LocationByParentMap locationByParentMap = TYPES.get(model.getClass()); if (locationByParentMap == null) { return null; } if (suggestedParentType != null && locationByParentMap.containsKey(suggestedParentType)) { return locationByParentMap.get(suggestedParentType); } for (Entry<Class<? extends BaseResource>, ApiLocationMetadata> entry : locationByParentMap.entrySet()) { if (entry.getKey() != NO_PARENT && getParent(model, entry.getKey()) != null) { return entry.getValue(); } } return locationByParentMap.get(NO_PARENT); } private static ApiLocationMetadata getLocationMetadata(BaseResource model) { return getLocationMetadata(model, null); } /** * Computes the path for the given object. For example, for a tag of a virtual machine returns the path * {@code /ovirt-engine/api/vms/{vm:id}/tags/{tag:id}}. * * @param object the object * @return the path for the object, or {@code null} if the path can't be determined */ public static String getPath(BaseResource object) { return getPath(object, null); } /** * Computes the path for the given object, using the given type to find out what is the type of the parent. * * @param entity the object * @param suggestedParentType the suggested parent type * @return the path for the object, or {@code null} if the path can't be determined */ public static String getPath(BaseResource entity, Class<? extends BaseResource> suggestedParentType) { ApiLocationMetadata locationMetadata = getLocationMetadata(entity, suggestedParentType); if (locationMetadata != null) { if (locationMetadata.getParentType() != NO_PARENT) { return getPathConsideringParent(entity, locationMetadata); } else { return getPathWithoutParent(entity, locationMetadata); } } else { return null; } } private static String getPathWithoutParent(BaseResource entity, ApiLocationMetadata locationMetadata) { Current current = CurrentManager.get(); return current.getAbsolutePath( getRelativePath(locationMetadata.getCollectionServiceClass()), entity.getId() ); } private static String getPathConsideringParent(BaseResource entity, ApiLocationMetadata locationMetadata) { BaseResource parent = getParent(entity, locationMetadata.getParentType()); if (parent == null) { return null; } ApiLocationMetadata parentLocationMetadata = getLocationMetadata(parent); if (parentLocationMetadata == null) { return null; } String parentPath = getPath(parent); if (parentPath == null) { return null; } String relativePath = getRelativePath(locationMetadata.getCollectionServiceClass(), parentLocationMetadata.getEntityServiceClass()); return String.join("/", parentPath, relativePath, entity.getId()); } /** * Set the href attribute on the supplied object * * e.g. set href = '/restapi-definition/vms/{vm_id}/tags/{tag_id}' on a VM tag * * @param model the object * @param suggestedParentType the suggested parent type */ private static void setHref(BaseResource model, String path) { if (path != null) { model.setHref(path); } } /** * Construct the set of action links for an object * * @param model the object * @param suggestedParentType the suggested parent type */ private static void setActions(BaseResource model, String path) { ApiLocationMetadata collection = getCollection(model); if (collection != null) { ActionsBuilder actionsBuilder = new ActionsBuilder(path, collection.getEntityServiceClass()); model.setActions(actionsBuilder.build()); } } /** * Adds the set of action links for an object * * @param model the object to add actions to * @param collection the object to get implemented methods from */ public static <R extends ActionableResource> void addActions(R model, Object collection) { Current current = CurrentManager.get(); String base = current.getPrefix() + current.getPath(); if (base != null) { ActionsBuilder actionsBuilder = new ActionsBuilder(base, model.getClass(), collection.getClass()); model.setActions(actionsBuilder.build()); } } /** * Set the href attribute on the object (and its inline objects) * and construct its set of action links * * @param model the object * @return the object, with href attributes and action links */ public static <R extends BaseResource> R addLinks(R model) { return addLinks(model, null); } public static <R extends BaseResource> R addLinks(R model, Class<? extends BaseResource> suggestedParentType) { return addLinks(model, suggestedParentType, true); } public static <R extends BaseResource> R addLinks(R model, Class<? extends BaseResource> suggestedParentType, boolean addActions) { String path = getPath(model, suggestedParentType); if (path != null) { model.setHref(path); if (addActions) { setActions(model, path); } } for (BaseResource inline : getInlineResources(model)) { if (inline.getId() != null) { path = getPath(inline, null); if (path!=null) { inline.setHref(path); } } for (BaseResource grandParent : getInlineResources(inline)) { unsetInlineResource(inline, grandParent.getClass()); } } return model; } /** * A #Map sub-class which holds location meta-data by API entity. * For efficient access each entity contains its metadata objects in a map, * with parent-type as key. For example, the following is an entry in * EntityLocationMap for the entity 'Group': * * ------------------------------------------------- * Group: * NO_PARENT: * parent: NO_PARENT * resource_single : GroupResource * resource_collection: GroupsResource * Domain: * parent: Domain * resource_single : DomainGroupResource * resource_collection: DomainGroupsResource * ------------------------------------------------- * * Out of which the following are entries in LocationByParentMap: * *-------------------------------------------------- * NO_PARENT: * parent: NO_PARENT * resource_single : GroupResource * resource_collection: GroupsResource *-------------------------------------------------- *-------------------------------------------------- * Domain: * parent: Domain * resource_single : DomainGroupResource * resource_collection: DomainGroupsResource *-------------------------------------------------- * * Out of which the following are instances of ApiLocationMetadata: * *-------------------------------------------------- * parent: NO_PARENT * resource_single : GroupResource * resource_collection: GroupsResource *-------------------------------------------------- *-------------------------------------------------- * parent: Domain * resource_single : DomainGroupResource * resource_collection: DomainGroupsResource *-------------------------------------------------- */ private static class EntityLocationMap extends HashMap<Class<?>, LocationByParentMap> {} /** * A map which holds entity location meta-data according to parent-type. * This is a utility map, which exists only for performance reasons, and is always * used in a broader context. An instance of this map represents location * metadata for a specific entity, but the entity-type is not saved within the map itself, * meaning that looking at an instance of LocationByParentMap without the context in which * it was created, one could not tell which entity the map describes. */ private static class LocationByParentMap extends LinkedHashMap<Class<? extends BaseResource>, ApiLocationMetadata> { public LocationByParentMap() { super(); } public LocationByParentMap(Class<?> serviceClass, Class<?> collectionClass, Class<? extends BaseResource> parentType) { super(); add(serviceClass, collectionClass, parentType); } public LocationByParentMap(Class<?> resourceType, Class<?> collectionType) { this(resourceType, collectionType, NO_PARENT); } public void add(Class<?> resourceType, Class<?> collectionType, Class<? extends BaseResource> parentType) { put(parentType, new ApiLocationMetadata(resourceType, collectionType, parentType)); } } /** * A container of meta-data for a location in the API tree: * 1) the Service class which handles single entities in this location. * 2) the Service class which handles the collection of entities in this location. * 3) the parent-type of entities in this location (if any). * e.g: for VMs in root: VmResource, VmsResource, parentType=null. * for VM-tags: AssignedTagResource, AssignedTagsResource, parentType=VM. */ private static class ApiLocationMetadata { private final Class<?> entityServiceClass; private final Class<?> collectionServiceClass; private final Class<?> parentType; public ApiLocationMetadata(Class<?> entityServiceClass, Class<?> collectionServiceClass, Class<?> parentType) { this.entityServiceClass = entityServiceClass; this.collectionServiceClass = collectionServiceClass; this.parentType = parentType; } public Class<?> getEntityServiceClass() { return entityServiceClass; } public Class<?> getCollectionServiceClass() { return collectionServiceClass; } public Class<?> getParentType() { return parentType; } } }